1 Java字符串編碼原理
??在Java中,字符的數(shù)據(jù)類型是char,而char類型的編碼是 Unicode 編碼,因此每一個char類型數(shù)據(jù)2字節(jié)16位,對應(yīng)在內(nèi)存中的數(shù)據(jù)就是字符的 Unicode 的碼值。而String類型的底層是一個char數(shù)組,因此String類型在內(nèi)存中的存儲形式是一系列字符對應(yīng)的 Unicode 碼值。
例如:中文字符串 "長風(fēng)幾萬里" ,表1是這5個中文字分別對應(yīng)的 GBK ,Unicode,和 UTF-8 編碼的碼值
表1:"長風(fēng)幾厘米"的碼表
| 字符 | GBK | Unicode | UTF-8 |
|---|---|---|---|
| 長 | B3A4 | 957F | E995BF |
| 風(fēng) | B7E7 | 98CE | E9A38E |
| 幾 | BCB8 | 51E0 | E587A0 |
| 萬 | CDF2 | 4E07 | E4B887 |
| 里 | C0EF | 91CC | E9878C |
我們新建Main函數(shù)運行如下代碼:其中 Java 源文件的文件編碼格式為 UTF-8,Jvm 默認的字符集編碼格式也為 UTF-8 。
char[] cs = str.toCharArray();
System.out.println(Arrays.toString(Num2Hex.chars2HexStrings(cs)));
byte[] bUTF = str.getBytes("UTF-8");
System.out.println(Arrays.toString(Num2Hex.bytes2HexStrings(bUTF)));
byte[] bGBK = str.getBytes("GBK");
System.out.println(Arrays.toString(Num2Hex.bytes2HexStrings(bGBK)));
運行結(jié)果如下:
[0x95, 0x7F, 0x98, 0xCE, 0x51, 0xE0, 0x4E, 0x07, 0x91, 0xCC]
[0xE9, 0x95, 0xBF, 0xE9, 0xA3, 0x8E, 0xE5, 0x87, 0xA0, 0xE4, 0xB8, 0x87, 0xE9, 0x87, 0x8C]
[0xB3, 0xA4, 0xB7, 0xE7, 0xBC, 0xB8, 0xCD, 0xF2, 0xC0, 0xEF]
如果我們把源文件的編碼格式改為GBK,運行結(jié)果不變
??也就是說對于String類型的字符串,他在內(nèi)存和 .class 文件中的編碼都是 Unicode 編碼,與源文件無關(guān),String類型的getBytes()方法,會將字符從 Unicode 編碼轉(zhuǎn)成參數(shù)指定名稱的字符集編碼的碼值(如果沒有指明字符集編碼,會按照 Jvm 的字符集編碼,如果沒有指定 Jvm 的字符集編碼,會根據(jù)操作系統(tǒng)的字符集編碼)然后返回這些碼值對應(yīng)的字節(jié)數(shù)組,由于 UTF-8 是3個字節(jié)表示一個漢字,所以返回的字節(jié)數(shù)組長度是15,而 GBK 編碼是2個字節(jié)表示一個漢字,所以返回的字節(jié)數(shù)組長度是10。
2 亂碼問題解決方法
??如何解決常見的亂碼問題呢?Java 字符編碼是 Unicode,字符集編碼我們最最常見的有 GBK,UTF-8,ISO-8859-1,解決亂碼的方法如下。
??首先搞清楚字符的數(shù)據(jù)源,例如"長風(fēng)幾萬里",如果字符數(shù)據(jù)源來自于String類型的常量,那么無論是在.class文件中還是在內(nèi)存中都是 Unicode 編碼,對應(yīng)的計算機數(shù)據(jù)用16進制表示就是{0x957F, 0x98CE, 0x51E0, 0x4E07, 0x91CC},如果是外部數(shù)據(jù)源,如果這5個字儲存在文件 a.txt 中并且 a.txt 的文件編碼假如是 GBK 的話,則文件 a.txt 存儲的計算機數(shù)據(jù)實際是這5個字符對應(yīng)的 GBK 的碼值,用16進制表示就是[0xB3, 0xA4, 0xB7, 0xE7, 0xBC, 0xB8, 0xCD, 0xF2, 0xC0, 0xEF],如果是 UTF-8 的話那么 a.txt 的內(nèi)容實際是[0xE9, 0x95, 0xBF, 0xE9, 0xA3, 0x8E, 0xE5, 0x87, 0xA0, 0xE4, 0xB8, 0x87, 0xE9, 0x87, 0x8C]。我們假設(shè) a.txt 編碼 是GBK,我們用 Java 讀取 a.txt 的文件內(nèi)容的字節(jié)保存在byte[]中,那么byte[]中的內(nèi)容還是這5個字符對應(yīng)的 GBK 的碼值[0xB3, 0xA4, 0xB7, 0xE7, 0xBC, 0xB8, 0xCD, 0xF2, 0xC0, 0xEF],如果我們讀取 a.txt 的文件內(nèi)容保存在char[]或者String(String的底層就是char[],String中的value就是保存在char[]中)中,那么我們就必須指明 a.txt 的字符集編碼,在字符集編碼正確的情況下,由于Java讀取外部數(shù)據(jù)源先獲得是數(shù)據(jù)源的字節(jié)流,因此 Java 還會將字節(jié)流轉(zhuǎn)為char[],就是要將[0xB3, 0xA4, 0xB7, 0xE7, 0xBC, 0xB8, 0xCD, 0xF2, 0xC0, 0xEF]轉(zhuǎn)成[0x957F, 0x98CE, 0x51E0, 0x4E07, 0x91CC],再保存在char[]或String里,如果不能正確指明數(shù)據(jù)源的字符集編碼那么就會轉(zhuǎn)成亂碼。