寫過兩篇關於編碼的文章了,以為自己比較了解編碼了呢?!
結果今天又結結實實的上了一課。
以前轉來轉去解決的問題終歸還是簡單的情形。即iso-8859-1轉utf-8,或者iso-8859-1轉gbk,gb2312之類。這種無損轉換,一行代碼就搞定。
今天遇到了gbk轉utf-8。無論怎么轉,都是亂碼。
一、亂碼的原因
gbk的中文編碼是一個漢字用【2】個字節表示,例如漢字“內部”的gbk編碼16進制的顯示為c4 da b2 bf
utf-8的中文編碼是一個漢字用【3】個字節表示,例如漢字“內部”的utf-8編碼16進制的顯示為e5 86 85 e9 83 a8
很顯然,gbk是無法直接轉換成utf-8,少字節變為多字節,誰知道缺少的字節是什么啊?!
二、轉換的辦法
有辦法實現“有損”轉換嗎?答案是肯定的。
1.首先將gbk字符串getBytes()得到兩個原始字節,轉換成二進制字符流,共16位。
2.根據UTF-8的漢字編碼規則,首字節以1110開頭,次字節以10開頭,第3字節以10開頭。在原始的2進制字符串中插入標志位。最終的長度從16--->16+4+2+2=24。
3.轉換完成,實際情況需要考慮更多因素,例如字符串是漢字和數字的混合體,需要識別處理數字。
三、不要重復發明輪子
這段代碼測試可用還很好用,需要的可以參考。
1 public static String getUTF8StringFromGBKString(String gbkStr) { 2 try { 3 return new String(getUTF8BytesFromGBKString(gbkStr), "UTF-8"); 4 } catch (UnsupportedEncodingException e) { 5 throw new InternalError(); 6 } 7 } 8 9 public static byte[] getUTF8BytesFromGBKString(String gbkStr) { 10 int n = gbkStr.length(); 11 byte[] utfBytes = new byte[3 * n]; 12 int k = 0; 13 for (int i = 0; i < n; i++) { 14 int m = gbkStr.charAt(i); 15 if (m < 128 && m >= 0) { 16 utfBytes[k++] = (byte) m; 17 continue; 18 } 19 utfBytes[k++] = (byte) (0xe0 | (m >> 12)); 20 utfBytes[k++] = (byte) (0x80 | ((m >> 6) & 0x3f)); 21 utfBytes[k++] = (byte) (0x80 | (m & 0x3f)); 22 } 23 if (k < utfBytes.length) { 24 byte[] tmp = new byte[k]; 25 System.arraycopy(utfBytes, 0, tmp, 0, k); 26 return tmp; 27 } 28 return utfBytes; 29 }
PS:有點兒對不住原作者,找了很多代碼,一一測試,結果網頁都關閉了,如果有大神認領,我一定補充鏈接,而且深表感謝。