通過前面中文化、國際化問題解決的系列1-4,相信大家對字符集、字符編碼、字符解碼、字符亂碼、Java中文問題解決等都有了一個比較清晰的認識;但文中的信息并非包羅萬象,結(jié)合到自己平時對于字符集、編碼相關(guān)的一些疑惑,本篇對一些前文中并未提及的一些問題進行補充,以便讓該系列更加完善和全面;本文主要解決以下兩個問題:其一,解決UltraEdit菜單中的 文件 -> 轉(zhuǎn)換 子菜單中涉及的一些名詞疑惑,主要涉及EBCDIC 、OEM字符集、ANSI字符集、HZ編碼等;其二,補充關(guān)于URL編碼相關(guān)的一些知識點,涉及瀏覽器、Web服務器設置、Servlet規(guī)范等;
相信很多同學都有在用UltraEdit這個文本編輯器軟件,從菜單 文件 --> 轉(zhuǎn)換 進入即可看見很多的字符集間的轉(zhuǎn)換子菜單,不同版本間可能會有些差異,可以參考下圖:
從上圖中我們可以看到很多前面已經(jīng)提及的一些詞匯,比如ASCII、Unicode [Big Endian/Little Endian]、UTF-8等;但還有一些未提及的,下面就逐個介紹下:
基于Java開發(fā)的Web應用URL組成如下:
http://domain:port/contextPath/servletPath/pathInfo?queryString
其中各個部分含義如下:
Domain、Port:分別是域名和端口;
contextPath:應用上下文路徑,默認為應用名稱,比如我們的apps;但可以通過應用服務器的相關(guān)配置進行修改,一般線上環(huán)境會修改成/,此時相當于contextPath為空;
servletPath:Servlet路徑,一般在應用的web.xml文件中配置servlet-mapping;但由于現(xiàn)在的web應用一般都會用一些框架,比如Struts、Webwork等,此時各框架都會對此進行封裝,會在另外的配置文件中進行設置;但原理都是一樣的;
pathInfo:可以理解為最終接收用戶請求的具體執(zhí)行類,比如我們常說的Action;
queryString:get方式傳入的請求參數(shù);
以上各個部分中可能存在中文問題的是pathInfo、queryString兩個部分;
首先,我們來看下Servlet中和URL相關(guān)的一些api及其注意事項:
HttpServletRequest.setCharacterEncoding(); //僅僅只適用于設置post提交的request body的編碼而不是設置get方法提交的queryString的編碼。該方法還告訴應用服務器應該采用什么編碼解析post傳過來的內(nèi)容;注意:若沒有設定characterEncoding,則使用ISO-8859-1來解碼用戶輸入的表單,而不是使用系統(tǒng)默認的編碼。
HttpServletResponse.setContentType(); //告訴瀏覽器網(wǎng)頁中數(shù)據(jù)是什么編碼;表單提交時,根據(jù)ContentType指定的charset對表單中的數(shù)據(jù)編碼,然后發(fā)送給服務器。
HttpServletRequest.getParameter("name"); //返回的字符串為:queryString(包括get和post),其值經(jīng)過Servlet服務器URL Decode過的,默認編碼來源于應用服務器中的配置,比如tomcat中server.xml的URIEncoding。
HttpServletRequest.getPathInfo(); //返回的字符串為:pathinfo;由Servlet服務器解碼(decode)過的。默認編碼同上,tomcat中可設置useBodyEncodingForURI。
HttpServletRequest.getRequestURI(); //返回的字符串為:contextPath/servletPath/pathinfo;注意是瀏覽器提交過來的原始數(shù)據(jù),未被Servlet服務器URL Decode過。
對URL編碼【URL Encoding/Percent Encoding】時,使用以下規(guī)則:
字母數(shù)字字符 "a" 到 "z"、"A" 到 "Z" 和 "0" 到 "9" 保持不變。
特殊字符 "."、"-"、"*" 和 "_" 保持不變。
空格字符 " " 轉(zhuǎn)換為一個加號 "+"。
所有其他字符都是不安全的,因此首先使用一些編碼機制將它們轉(zhuǎn)換為一個或多個字節(jié)。然后每個字節(jié)用一個包含 3 個字符的字符串 "%xy" 表示,其中 xy 為該字節(jié)的兩位十六進制表示形式。推薦的編碼機制是 UTF-8。但是,出于兼容性考慮,如果未指定一種編碼,則使用相應平臺的默認編碼。
假定我們待請求URL為:http://localhost:8080/example/中國?name=中國;
Html內(nèi)content-type或meta中的charset=GBK;文件格式為ANSI/ASCII;
URL中的兩個漢字"中國"的各字符集下的編碼為:
漢字 | 編碼 | 二進制表示 |
中國 | UTF-8 | 0xe4 0xb8 0xad 0xe5 0x9b 0xbd[-28, -72, -83, -27, -101, -67] |
中國 | GBK | 0xd6 0xd0 0xb9 0xfa[-42, -48, -71, -6] |
中國 | ISO8859-1 | 0x3f 0x3f[63, 63][??] |
對于Get方式的URL請求有兩種情況,其一:用戶直接在瀏覽器地址欄中輸入URL,此時瀏覽器沒有編碼可參考,直接用瀏覽器的默認編碼進行解析并提交到服務端;其二:在form表單內(nèi)提交,只是form屬性method為GET,此時瀏覽器會參考目前html中對編碼的相關(guān)設置進行解析,比如content-type或meta中的charset。
以下就重點講講第二種方式的提交:
GET方式form submit:瀏覽器會對URL進行URL encoding,然后發(fā)送給服務器。
很顯然,不同的瀏覽器以及同一瀏覽器的不同設置,會影響最終URL中PathInfo的編碼,該編碼可能不會由我們應用來控制;對于queryString,則是可以由我們的應用來完全控制的,對于上面的事例:中文的IE和FIREFOX都是采用GBK編碼queryString。
若調(diào)整下上例中的假設條件,設置Html內(nèi)content-type或meta中的charset=UTF-8;
此時在IE中queryString會按照UTF-8進行編碼,即name=%E4%B8%AD%E5%9B%BD;
但是在非IE(Firefox、Chrome)中,此時提交時URL中會以中文直接提交,即name=中文;此時服務端的web服務器上肯定要進行相應的編碼配置,否則肯定會出現(xiàn)亂碼;
若設置Html內(nèi)content-type或meta中的charset=ISO-5899-1;
此時在IE、Firefox、Chrome中queryString都被用ISO-5899-1編碼了,即name= %26%2320013%3B%26%2322269%3B;
對于編碼串中的%26、%3B應該是百分號編碼【Percent Encoding】中的保留字符,分別對應&、;,兩者之間是經(jīng)過編碼的十進制碼;對于這點偶也不是十分肯定?要是有同學比較清楚,請告訴偶下,thx。
POST方式提交:表單中的參數(shù)值對是通過request body發(fā)送給服務器,此時瀏覽器會根據(jù)網(wǎng)頁的ContentType("text/html; charset=GBK")中指定的編碼進行對表單中的數(shù)據(jù)進行編碼,然后發(fā)給服務器。
在服務器端的程序中我們可以通過Request.setCharacterEncoding() 設置編碼,然后通過request.getParameter獲得正確的數(shù)據(jù)。
小結(jié):
相關(guān)文檔參考:
字符,字節(jié)和編碼:http://www.regexlab.com/zh/encoding.htm
各種字符集和編碼詳解:http://blog.csdn.net/ancky/archive/2008/01/11/2034809.aspx
深入淺出URL編碼:http://blog.csdn.net/yzhz/archive/2007/07/03/1676796.aspx
javascript html 相關(guān)編碼問題研究:http://stauren.net/log/fpev3c89q.html
J2EE Web組件中中文及相關(guān)的問題(系列):http://blog.csdn.net/whodsow/archive/2003/10/27/19465.aspx