【GBK轉(zhuǎn)UTF-8】
在很多論壇、網(wǎng)上經(jīng)常有網(wǎng)友問“ 為什么我使用 new String(tmp.getBytes("ISO-8859-1"), "UTF-8") 或者 new String(tmp.getBytes("ISO-8859-1"), "GBK")可以得到正確的中文,但是使用 new String(tmp.getBytes("GBK"), "UTF-8") 卻不能將GBK轉(zhuǎn)換成UTF-8呢?”
參考前面的【Java基礎(chǔ)專題】編碼與亂碼(03)----String的toCharArray()方法測(cè)試 一文,我們就知道原因了。因?yàn)槿绻蛻舳耸褂肎BK、UTF-8編碼,編碼后的字節(jié)經(jīng)過ISO-8859-1傳輸,再用原來(lái)相同的編碼方式進(jìn)行解碼,這個(gè)過程是“無(wú)損的轉(zhuǎn)換”---- 因?yàn)樵己妥罱K的編碼方式相同。
但是如果客戶端使用GBK編碼,到了服務(wù)器端要轉(zhuǎn)換成UTF-8,或者相反的過程。想一想,字節(jié)還是那些字節(jié),但是編碼的規(guī)則變了。原來(lái)GBK編碼后的4個(gè)字節(jié)要用UTF-8的每個(gè)字符3個(gè)字節(jié)的規(guī)則編碼,怎么能不亂碼呢?
所以從現(xiàn)在開始,不要再犯這種錯(cuò)誤了。new String(tmp.getBytes("GBK"), "UTF-8") 這個(gè)過程,JVM內(nèi)部是不會(huì)幫你自動(dòng)對(duì)字節(jié)進(jìn)行擴(kuò)展以適應(yīng)UTF-8的編碼的。正確的方法應(yīng)該是根據(jù)UTF-8的編碼規(guī)則進(jìn)行字節(jié)的擴(kuò)充,即手動(dòng)從2個(gè)字節(jié)變成3個(gè)字節(jié),然后再轉(zhuǎn)換成十六進(jìn)制的UTF-8編碼。
在這個(gè)專題的第一篇文章【Java基礎(chǔ)專題】編碼與亂碼(01)---編碼基礎(chǔ) 開頭,我們就已經(jīng)介紹了這個(gè)規(guī)則:
①得到每個(gè)字符的2進(jìn)制GBK編碼
②將該16進(jìn)制的GBK編碼轉(zhuǎn)換成2進(jìn)制的字符串(2個(gè)字節(jié))
③分別在字符串的首位插入110,在第9位插入10,在第17位插入10三個(gè)字符串,得到3個(gè)字節(jié)
④將這3個(gè)字節(jié)分別轉(zhuǎn)換成16進(jìn)制編碼,得到最終的UTF-8編碼。

下面給出一個(gè)從網(wǎng)絡(luò)上得到的Java轉(zhuǎn)碼方法,原文鏈接見:http://jspengxue.javaeye.com/blog/40781。下面的代碼做了小小的修改
package example.encoding;


/** *//**
* The Class CharacterEncodeConverter.
*/

public class CharacterEncodeConverter
{


/** *//**
* The main method.
*
* @param args the arguments
*/

public static void main(String[] args)
{


try
{
CharacterEncodeConverter convert = new CharacterEncodeConverter();
byte[] fullByte = convert.gbk2utf8("中文");
String fullStr = new String(fullByte, "UTF-8");
System.out.println("string from GBK to UTF-8 byte: " + fullStr);


} catch (Exception e)
{
e.printStackTrace();
}
}


/** *//**
* Gbk2utf8.
*
* @param chenese the chenese
*
* @return the byte[]
*/

public byte[] gbk2utf8(String chenese)
{
// Step 1: 得到GBK編碼下的字符數(shù)組,一個(gè)中文字符對(duì)應(yīng)這里的一個(gè)c[i]
char c[] = chenese.toCharArray();
// Step 2: UTF-8使用3個(gè)字節(jié)存放一個(gè)中文字符,所以長(zhǎng)度必須為字符的3倍
byte[] fullByte = new byte[3 * c.length];
// Step 3: 循環(huán)將字符的GBK編碼轉(zhuǎn)換成UTF-8編碼

for (int i = 0; i < c.length; i++)
{
// Step 3-1:將字符的ASCII編碼轉(zhuǎn)換成2進(jìn)制值
int m = (int) c[i];
String word = Integer.toBinaryString(m);
System.out.println(word);

// Step 3-2:將2進(jìn)制值補(bǔ)足16位(2個(gè)字節(jié)的長(zhǎng)度)
StringBuffer sb = new StringBuffer();
int len = 16 - word.length();

for (int j = 0; j < len; j++)
{
sb.append("0");
}
// Step 3-3:得到該字符最終的2進(jìn)制GBK編碼
// 形似:1000 0010 0111 1010
sb.append(word);
// Step 3-4:最關(guān)鍵的步驟,根據(jù)UTF-8的漢字編碼規(guī)則,首字節(jié)
// 以1110開頭,次字節(jié)以10開頭,第3字節(jié)以10開頭。在原始的2進(jìn)制
// 字符串中插入標(biāo)志位。最終的長(zhǎng)度從16--->16+3+2+2=24。
sb.insert(0, "1110");
sb.insert(8, "10");
sb.insert(16, "10");
System.out.println(sb.toString());

// Step 3-5:將新的字符串進(jìn)行分段截取,截為3個(gè)字節(jié)
String s1 = sb.substring(0, 8);
String s2 = sb.substring(8, 16);
String s3 = sb.substring(16);

// Step 3-6:最后的步驟,把代表3個(gè)字節(jié)的字符串按2進(jìn)制的方式
// 進(jìn)行轉(zhuǎn)換,變成2進(jìn)制的整數(shù),再轉(zhuǎn)換成16進(jìn)制值
byte b0 = Integer.valueOf(s1, 2).byteValue();
byte b1 = Integer.valueOf(s2, 2).byteValue();
byte b2 = Integer.valueOf(s3, 2).byteValue();
// Step 3-7:把轉(zhuǎn)換后的3個(gè)字節(jié)按順序存放到字節(jié)數(shù)組的對(duì)應(yīng)位置
byte[] bf = new byte[3];
bf[0] = b0;
bf[1] = b1;
bf[2] = b2;
fullByte[i * 3] = bf[0];
fullByte[i * 3 + 1] = bf[1];
fullByte[i * 3 + 2] = bf[2];
// Step 3-8:返回繼續(xù)解析下一個(gè)中文字符
}
return fullByte;
}
}
最終的測(cè)試結(jié)果是正確的:string from GBK to UTF-8 byte: 中文。
但是這個(gè)方法并不是完美的!要知道這個(gè)規(guī)則只對(duì)中文起作用,如果傳入的字符串中包含有單字節(jié)字符,如a+3中文,那么解析的結(jié)果就變成:string from GBK to UTF-8 byte: ?????????中文了。為什么呢?道理很簡(jiǎn)單,這個(gè)方法對(duì)原本在UTF-8中應(yīng)該用單字節(jié)表示的數(shù)字、英文字符、符號(hào)都變成3個(gè)字節(jié)了,所以這里有9個(gè)?,代表被轉(zhuǎn)換后的a、+、3字符。
所以要讓這個(gè)方法更加完美,最好的方法就是加入對(duì)字符Unicode區(qū)間的判斷
UCS-2編碼(16進(jìn)制) |
UTF-8 字節(jié)流(二進(jìn)制) |
0000 - 007F |
0xxxxxxx |
0080 - 07FF |
110xxxxx 10xxxxxx |
0800 - FFFF |
1110xxxx 10xxxxxx 10xxxxxx |
漢字的Unicode編碼范圍為\u4E00-\u9FA5 \uF900-\uFA2D,如果不在這個(gè)范圍內(nèi)就不是漢字了。
【UTF-8轉(zhuǎn)GBK】
道理和上面的相同,只是一個(gè)逆轉(zhuǎn)的過程,不多說(shuō)了
但是最終的建議還是:能夠統(tǒng)一編碼就統(tǒng)一編碼吧!要知道編碼的轉(zhuǎn)換是相當(dāng)?shù)暮臅r(shí)的工作
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
posted on 2010-02-22 23:00
Paul Lin 閱讀(37031)
評(píng)論(11) 編輯 收藏 所屬分類:
J2SE