[Charset]UTF-8, UTF-16, UTF-16LE, UTF-16BE的區別
今天遇到的麻煩事 ,好久沒有使用servelt類了,今天偶感,就使用了,在報錯之後在jsp中設定的編碼形式是:<%@ page language="java" import="java.util.*" pageEncoding="GBK" contentType="text/html; charset=GBK"%> <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
在servlet中設定的是:response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
結果後臺報的錯誤還是:xml的格式為utf-16ln , 不能轉換為utf-8
我就納悶了,我有沒有使用xml格式的資料,而且每個頁面到使用編碼統一,沒辦法,就是不通過,最後把編碼都去掉了,呵呵,查到資料了,汗啊。
然後,我有把每個頁面的編碼重新統一了,狂汗,又查到資料了。同時也查看了一些編碼,如下:
charset裡的問題, 一般我們都用unicode來作為統一編碼, 但unicode也有多種表現形式首先, 我們說的unicode, 其實就是utf-16, 但最通用的卻是utf-8,
原因: 我猜大概是英文佔的比例比較大, 這樣utf-8的儲存優勢比較明顯, 因為utf-16是固定16位的(雙位元組), 而utf-8則是看情況而定, 即可變長度, 常規的128個ASCII只需要8位(單位元組), 而漢字需要24位
UTF-16, UTF-16LE, UTF-16BE, 及其區別BOM
同樣都是unicode, 為什麼要搞3種這麼麻煩?
先說UTF-16BE (big endian), 比較好理解的, 俗稱大頭
比如說char 'a', ascii為
0x61, 那麼它的utf-8, 則為 [0x61], 但utf-16是16位的, 所以為[0x00, 0x61]
再說UTF-16LE(little endian), 俗稱小頭, 這個是比較常用的
還是char 'a', 它的程式碼卻反過來: [0x61, 0x00], 據說是為了提高速度而迎合CPU的胃口, CPU就是這到倒著吃資料的, 這裡面有彙編的知識, 不多說
然後說UTF-16, 要從程式碼裡自動判斷一個檔案到底是UTF-16LE還是BE, 對於單純的英文字元來說還比較好辦, 但要有特殊字元, 圖形符號, 漢字, 法文, 俄語, 火星語之類的話, 相信各位都很頭痛吧, 所以, unicode組織引入了BOM的概念, 即byte order mark, 顧名思義, 就是表名這個檔案到底是LE還是BE的,
其方法就是, 在UTF-16檔案的頭2個位元組裡做個標記: LE [0xFF, 0xFE], BE [0xFE, 0xFF]
理解了這個後, 在java裡遇到utf-16還是會遇到麻煩, 因為要在檔案裡面單獨判斷頭2個再位元組是很不流暢的
小結:
Java程式碼
- InputStreamReader reader=new InputStreamReader(fin, charset)
1. 如果這個UTF-16檔案裡帶有BOM的話, charset就用"UTF-16", java會自動根據BOM判斷LE還是BE, 如果你在這裡指定了"UTF-16LE"或"UTF-16BE"的話, 猜錯了會生成亂七八糟的檔案, 哪怕猜對了, java也會把頭2個位元組當成文字輸出給你而不會略過去, 因為[FF FE]或[FE FF]這2個程式碼沒有內容, 所以, windows會用"?"代替給你
2. 如果這個UTF-16檔案裡不帶BOM的話, 則charset就要用"UTF-16LE"或"UTF-16BE"來指定LE還是BE的編碼方式
另外, UTF-8也有BOM的, [0xEF, 0xBB, 0xBF], 但可有可無, 但用windows的notepad另存為時會自動幫你加上這個, 而很多非windows平臺的UTF8檔案又沒有這個BOM, 真是難為我們這些程式設計師啊
錯誤的例子
1. 檔案A, UTF16格式, 帶BOM LE,
InputStreamReader reader=new InputStreamReader(fin, "utf-16le")
會多輸出一個"?"在第一個位元組, 原因: java沒有把頭2位當成BOM
2. 檔案A, UTF16格式, 帶BOM LE,
InputStreamReader reader=new InputStreamReader(fin, "utf-16be")
會出亂碼, 原因: 位元組的高低位弄反了, 'a' 在檔案裡 [0x61, 0x00], 但java以為'a'應該是[0x00 0x61]
3. 檔案A, UTF16格式, 帶BOM BE,
InputStreamReader reader=new InputStreamReader(fin, "utf-16le")
會出亂碼, 原因: 位元組的高低位弄反了, 'a' 在檔案裡 [0x00, 0x61], 但java以為'a'應該是[0x61 0x00]
4. 檔案A, UTF16格式, 帶BOM BE,
InputStreamReader reader=new InputStreamReader(fin, "utf-16be")
會多輸出一個"?"在第一個位元組, 原因: java沒有把頭2位當成BOM
5. 檔案A, UTF16格式, LE 不帶BOM,
InputStreamReader reader=new InputStreamReader(fin, "utf-16")
會出亂碼, 因為utf-16對於java來說, 預設為be(1.6JDK, 以後的說不準)
但windows的notepad開啟正常, 因為notepad預設為le, - -#
6. 檔案A, UTF16格式, BE 不帶BOM,
InputStreamReader reader=new InputStreamReader(fin, "utf-16")
恭喜你, 蒙對了
但winodws的notepad開啟時, 每個字元中間都多了一個" ", 因為notepad把它當成ASNI了
1、一個http請求經過的幾個環節:
瀏覽器(ie firefox)【get/post】------------>Servlet伺服器------------------------------->瀏覽器顯示
編碼 解碼成unicode,然後將顯示的內容編碼 解碼
(1) 瀏覽器把URL(以及post提交的內容)經過編碼後傳送給伺服器。
(2) 這裡的Servlet伺服器實際上指的是由Servlet伺服器提供的servlet實現ServletRequestWrapper,不同應用伺服器的servlet實現不同,這些servlet的實現把這些內容解碼轉換為unicode,處理完畢後,然後再把結果(即網頁)編碼返回給瀏覽器。
(3) 瀏覽器按照指定的編碼顯示該網頁。
當對字串進行編碼和解碼的時候都涉及到字符集,通常使用的字符集為ISO8859-1、GBK、UTF-8、UNICODE。
2、URL的組成:
域名:埠/contextPath/servletPath/pathInfo?queryString
說明:
1、ContextPath是在Servlet伺服器的配置檔案中指定的。
對於weblogic:
contextPath是在應用的weblogic.xml中配置。
<context-root>/</context-root>
對於tomcat:
contextPath是在server.xml中配置。
<Context path="/" docBase="D:/server/blog.war" debug="5" reloadable="true" crossContext="true"/>
對於jboos:
contextPath是在應用的jboss-web.xml中配置。
<jboss-web>
<context-root>/</context-root>
</jboss-web>
2、ServletPath是在應用的web.xml中配置。
<servlet-mapping>
<servlet-name>Example</servlet-name>
<url-pattern>/example/*</url-pattern>
</servlet-mapping>
2、Servlet API
我們使用以下servlet API獲得URL的值及引數。
request.getParameter("name"); // 獲得queryString的引數值(來自於get和post),其值經過Servlet伺服器URL Decode過的
request.getPathInfo(); // 注意:pathinfo返回的字串是經過Servlet伺服器URL Decode過的。
requestURI = request.getRequestURI(); // 內容為:contextPath/servletPath/pathinfo 瀏覽器提交過來的原始資料,未被Servlet伺服器URL Decode過。
3、開發人員必須清楚的servlet規範:
(1) HttpServletRequest.setCharacterEncoding()方法 僅僅只適用於設定post提交的request body的編碼而不是設定get方法提交的queryString的編碼。該方法告訴應用伺服器應該採用什麼編碼解析post傳過來的內容。很多文章並沒有說明這一點。
(2) HttpServletRequest.getPathInfo()返回的結果是由Servlet伺服器解碼(decode)過的。
(3) HttpServletRequest.getRequestURI()返回的字串沒有被Servlet伺服器decoded過。
(4) POST提交的資料是作為request body的一部分。
(5) 網頁的Http頭中ContentType("text/html; charset=GBK")的作用:
(a) 告訴瀏覽器網頁中資料是什麼編碼;
(b) 表單提交時,通常瀏覽器會根據ContentType指定的charset對錶單中的資料編碼,然後傳送給伺服器的。
這裡需要注意的是:這裡所說的ContentType是指http頭的ContentType,而不是在網頁中meta中的ContentType。