1. 程式人生 > >java gbk轉utf-8亂碼問題

java gbk轉utf-8亂碼問題

最近在做一個反饋功能,把資料反饋到對方公司網站,我公司是GBK編碼,對方公司是UTF-8編碼。因此,我需要將GBK編碼資料轉換成UTF-8編碼資料,這樣對方網站才不會亂碼。最簡單的方法是將HttpClient的ContentCharset設定為utf-8;如果ContentCharset是gbk並且又不想設定為utf-8,那麼就需要將資料轉換成UTF-8編碼再發到對方網站。
問題出現:GBK轉UTF-8時,奇數箇中文會亂碼,偶數箇中文不會亂碼。
三個中文
public static void encodeError() throws UnsupportedEncodingException {
String gbk = "我來了";
String utf8 = new String(gbk.getBytes("UTF-8")); //模擬UTF-8編碼的網站顯示
System.out.println(new String(utf8.getBytes(),"UTF-8"));}/*我來??*/ 前面三個中文,後面一箇中文,都是奇數
public static void encodeError2() throws UnsupportedEncodingException {
String gbk = "今年是2011年";
String utf8 = new String(gbk.getBytes("UTF-8"));

//模擬UTF-8編碼的網站顯示
System.out.println(new String(utf8.getBytes(),"UTF-8"));

/
今年??011??
*/
原因:為什麼只有奇數箇中文才亂碼,偶數個卻不亂碼?下面來分析原因
public static void analyze() throws UnsupportedEncodingException {
String gbk = "我來了";
String utf8 = new String(gbk.getBytes("UTF-8"));
for (byte b : gbk.getBytes("UTF-8")) {
System.out.print(b + " ");
}
System.out.println();
for (byte b : utf8.getBytes()) {
System.out.print(b + " ");
}
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122
-26 -120 -111 -26 -99 -91 -28 -70 63 !
*/ 注意最後一個位元組不同,上面一行才是正確的UTF-8編碼。那麼為什麼下面一行最後一個位元組是63,而不是-122呢?這就是導致亂碼的原因所在。
GBK編碼是一箇中文2個位元組,而UTF-8編碼是一箇中文3個位元組,當我們呼叫getBytes("UTF-8")方法時,會通過計算來增加位元組,使得從GBK的2個位元組變成UTF-8對應的3個位元組。因此,上例3箇中文輸出了9個位元組。

這裡講一下怎麼通過計算增加位元組,不深究的讀者可以跳過此段。為了醒目,直接用程式碼講解
public static void gbk2Utf() throws UnsupportedEncodingException {
String gbk = "我來了";
char[] c = gbk.toCharArray();& L( x1 B( N7 {
byte[] fullByte = new byte[3*c.length];
for (int i=0; i<c.length; i++) {
String binary = Integer.toBinaryString(c[i]);
StringBuffer sb = new StringBuffer();
int len = 16 - binary.length();
//前面補零
for(int j=0; j<len; j++){
sb.append("0");
}
sb.append(binary);
//增加位,達到到24位3個位元組
sb.insert(0, "1110");
sb.insert(8, "10");
sb.insert(16, "10");
fullByte[i*3] = Integer.valueOf(sb.substring(0, 8), 2).byteValue();//二進位制字串建立整型
fullByte[i*3+1] = Integer.valueOf(sb.substring(8, 16), 2).byteValue();
fullByte[i*3+2] = Integer.valueOf(sb.substring(16, 24), 2).byteValue();
} @, N* V4 r3 n0 T
//模擬UTF-8編碼的網站顯示
System.out.println(new String(fullByte,"UTF-8"));
}
現在我們來找出最後一個位元組是63,而不是-122的原因。
public static void analyze2() throws UnsupportedEncodingException {
String gbk = "我來了";
byte[] utfBytes = gbk.getBytes("UTF-8");//得到9個位元組;
String utf8 = new String(utfBytes);//問題就出在這
System.out.print(utf8);
}
/*
鎴戞潵浜?
*/ 因為檔案是GBK編碼,new String(utfBytes)預設就是new String(utfBytes,"GBK")。它會2個位元組2個位元組地轉換成字元,當位元組是奇數時最後1個位元組轉字元就會計算錯誤,然後直接賦予最後這個字元為?,對應ASCII程式碼就是63。

解決問題
保證位元組正確才是硬道理。當呼叫getBytes("UTF-8")轉換成位元組陣列後,建立ISO-8859-1編碼的字串,ISO-8859-1編碼是一個位元組對應一個字元,因此不會使最後一個位元組錯誤。
public static void correctEncode() throws UnsupportedEncodingException {
String gbk = "我來了";
String iso = new String(gbk.getBytes("UTF-8"),"ISO-8859-1");
for (byte b : iso.getBytes("ISO-8859-1")) {
System.out.print(b + " ");
}
System.out.println();

//模擬UTF-8編碼的網站顯示
System.out.println(new String(iso.getBytes("ISO-8859-1"),"UTF-8"));
}
/*
-26 -120 -111 -26 -99 -91 -28 -70 -122
我來了
*/