Java中文編碼小結
1. 只有 字符到字節 或者 字節到字符 的轉換才存在編碼轉碼;
2. Java String 采用 UTF-16 編碼方式存儲所有字符。unicode體系采用唯一的碼點表示唯一的字符信息, 碼點的存儲方式有UFT-16、UTF-8 等等。: A String represents a string in the UTF-16 format in which supplementary characters are represented bysurrogate pairs (see the section Unicode Character Representations in the Character class for more information). Index values refer to char code units, so a supplementary character uses two positions in a String. The String class provides methods for dealing with Unicode code points (i.e., characters), in addition to those for dealing with Unicode code units (i.e., char values).
3. String只有一種格式,可認為String是獨立於編碼系統的,通過 getBytes(String charsetName) 可實現編碼轉換。
4. String對象是內存數據,string之間不存在編碼變換問題。
5. 編碼轉換場景主要在 I/O , I/O 包括磁盤 I/O 和網絡 I/O:文件輸入輸出、屏幕、數據庫、瀏覽器、服務器。
6. 在內存中倒騰String數據是編碼無關的,比如壓縮編碼。
7. 編碼誤區: new String(str.getBytes("ISO-8859-1"), "GB18030") 這種用法是無意義的,甚至是錯誤的。這種用法是用GB18030編碼將ISO-8859-1編碼格式的字節數據強制轉換成unicode碼點,不亂碼是運氣!
9. 數據庫JDBC能夠處理 數據庫數據 <=> String 的正確互換。
9. OutputStreamWriter 和 InputStreamWriter 應該指定編碼格式,避免程序依賴操作系統默認編碼。
10. 用戶從瀏覽器端發起一個 HTTP 請求,需要存在編碼的地方是 URL、Cookie、Parameter。服務器端接受到 HTTP 請求后要解析 HTTP 協議,其中 URI、Cookie 和 POST 表單參數需要解碼,服務器端可能還需要讀取數據庫中的數據,本地或網絡中其它地方的文本文件,這些數據都可能存在編碼問題,當 Servlet 處理完所有請求的數據后,需要將這些數據再編碼通過 Socket 發送到用戶請求的瀏覽器里,再經過瀏覽器解碼成為文本。
11. tomcat: URL 的 URI 部分進行解碼的字符集是在 connector 的 <Connector URIEncoding=”UTF-8”/>
12. QueryString(GET 查詢參數) 的解碼字符集要么是 Header 中 ContentType 中定義的 Charset 要么就是默認的 ISO-8859-1,要使用 ContentType 中定義的編碼就要設置 connector 的 <Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true”/> 中的 useBodyEncodingForURI 設置為 true。
13. 不要在 Header 中傳遞非 ASCII 字符,如果一定要傳遞的話,我們可以先將這些字符用 org.apache.catalina.util.URLEncoder 編碼然后再添加到 Header 中,這樣在瀏覽器到服務器的傳遞過程中就不會丟失信息了,如果我們要訪問這些項時再按照相應的字符集解碼就好了。
14. POST 表單的編解碼: 通過 HTTP 的 BODY 傳遞到服務端的。當我們在頁面上點擊 submit 按鈕時瀏覽器首先將根據 ContentType 的 Charset 編碼格式對表單填的參數進行編碼然后提交到服務器端,在服務器端同樣也是用 ContentType 中字符集進行解碼。所以通過 POST 表單提交的參數一般不會出現問題,而且這個字符集編碼是我們自己設置的,可以通過 request.setCharacterEncoding(charset) 來設置。
15. HTTP BODY 的編解碼: 當用戶請求的資源已經成功獲取后,這些內容將通過 Response 返回給客戶端瀏覽器,這個過程先要經過編碼再到瀏覽器進行解碼。這個過程的編解碼字符集可以通過 response.setCharacterEncoding 來設置,它將會覆蓋 request.getCharacterEncoding 的值,並且通過 Header 的 Content-Type 返回客戶端,瀏覽器接受到返回的 socket 流時將通過 Content-Type 的 charset 來解碼,如果返回的 HTTP Header 中 Content-Type 沒有設置 charset,那么瀏覽器將根據 Html 的 <meta HTTP-equiv="Content-Type" content="text/html; charset=GBK" /> 中的 charset 來解碼。如果也沒有定義的話,那么瀏覽器將使用默認的編碼來解碼。<%@ page contentType="text/html; charset= GBK" %>。該設置和response.setCharacterEncoding("GBK")等效。
示例代碼
/** * @author zhenjing * * @date 2013-9-7 */ public class cnCodeTest { public static void toHex(char[] b) { for (int i = 0; i < b.length; i++) { System.out.printf("%x " , (int)b[i]); } System.out.println(); } public static void toHex(byte[] b) { for (int i = 0; i < b.length; i++) { System.out.printf("%x " , b[i]); } System.out.println(); } public static void encode() { String name = "I am 中文編碼"; toHex(name.toCharArray()); try { byte[] iso8859 = name.getBytes("ISO-8859-1"); toHex(iso8859); byte[] gb2312 = name.getBytes("GB2312"); toHex(gb2312); byte[] gbk = name.getBytes("GBK"); toHex(gbk); byte[] utf16 = name.getBytes("UTF-16"); toHex(utf16); byte[] utf8 = name.getBytes("UTF-8"); toHex(utf8); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { String cn = "中文編碼"; // 這里存在編碼轉換: 將文件存儲字節轉成unicode存入String對象內存. 采用文件編碼 char[] charArray = cn.toCharArray(); byte[] data = cn.getBytes(); System.out.println("print char array : " + cn); toHex(cn.toCharArray()); cn = "���ı���"; // 這里存在編碼轉換: 將文件存儲字節轉成unicode存入String對象內存. 采用文件編碼。 // 顯示亂碼是由於文件采用的編碼無法解碼文件存儲字節數據。故存到String的unicode也是亂碼的 charArray = cn.toCharArray(); System.out.println("print char array: " + cn); toHex(cn.toCharArray()); encode(); } }
