http://blog.csdn.net/zhangxiaoxiang/archive/2006/08/22/1106714.aspx
問題:
public class TestUTF8
{
?? public static void main(String [] args) throws Exception
?? {
????? ?byte[] bytes = {(byte)0xC0,(byte)0xB1};
?????? String str = new String(bytes,"UTF-8");
?????? System.out.println(str.getBytes("UTF-8").length);
?? }
}
程序運(yùn)行后打印的結(jié)果如下:
1
這個程序?qū)?兩個 字節(jié)的bytes數(shù)組按UTF-8編碼轉(zhuǎn)換成字符串,再將結(jié)果字符串轉(zhuǎn)換成UTF-8編碼的字節(jié)數(shù)組,打印出的這個字節(jié)數(shù)組的長度為?1 。為什么兩個字節(jié)的數(shù)組轉(zhuǎn)換成的字符串反轉(zhuǎn)回去的字節(jié)數(shù)組的長度卻為一呢??
答案:
這個問題應(yīng)該是由UTF-8編碼的特點(diǎn)造成的。
UTF-8是一種變長字節(jié)編碼方式。對于某一個字符的UTF-8編碼,如果只有一個字節(jié)則其最高二進(jìn)制位為0;如果是多字節(jié),其第一個字節(jié)從最高位開始,連續(xù)的二進(jìn)制位值為1的個數(shù)決定了其編碼的位數(shù),其余各字節(jié)均以10開頭。UTF-8最多可用到6個字節(jié)。
如表:
1字節(jié) 0xxxxxxx
2字節(jié) 110xxxxx 10xxxxxx
3字節(jié) 1110xxxx 10xxxxxx 10xxxxxx
4字節(jié) 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字節(jié) 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字節(jié) 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用來表示字符編碼的實(shí)際位數(shù)最多有31位,即上表中x所表示的位。除去那些控制位(每字節(jié)開頭的10等),這些x表示的位與UNICODE編碼是一一對應(yīng)的,位高低順序也相同。
實(shí)際將UNICODE轉(zhuǎn)換為UTF-8編碼時應(yīng)先去除高位0,然后根據(jù)所剩編碼的位數(shù)決定所需最小的UTF-8編碼位數(shù)。
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一個字節(jié)的UTF-8編碼(7個二進(jìn)制位)便可以表示。
對于上面的問題,代碼中給出的兩個字節(jié)是
十六進(jìn)制:C0 B1
二進(jìn)制:11000000 10110001
對比兩個字節(jié)編碼的表示方式:
110xxxxx 10xxxxxx
提取出對應(yīng)的UNICODE編碼:
00000 110001
可以看出此編碼并非“標(biāo)準(zhǔn)”的UTF-8編碼,因?yàn)槠涞谝粋€字節(jié)的“有效編碼”全為0,去除高位0后的編碼僅有6位。由前面所述,此字符僅用一個字節(jié)的UTF-8編碼表示就夠了。
JAVA在把字符還原為UTF-8編碼時,是按照“標(biāo)準(zhǔn)”的方式處理的,因此我們得到的是僅有1個字節(jié)的編碼。
大家可以試試運(yùn)行這段代碼:
public class TestUTF8 {
public static void main(String[] args) throws Exception {
byte[][] bytes = {
// 00110001
{(byte)0x31},
// 11000000 10110001
{(byte)0xC0,(byte)0xB1},
// 11100000 10000000 10110001
{(byte)0xE0,(byte)0x80,(byte)0xB1},
// 11110000 10000000 10000000 10110001
{(byte)0xF0,(byte)0x80,(byte)0x80,(byte)0xB1},
// 11111000 10000000 10000000 10000000 10110001
{(byte)0xF8,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0xB1},
// 11111100 10000000 10000000 10000000 10000000 10110001
{(byte)0xFC,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0xB1},
};
for (int i = 0; i < 6; i++) {
String str = new String(bytes[i], "UTF-8");
System.out.println("原數(shù)組長度:" + bytes[i].length +
"\t轉(zhuǎn)換為字符串:" + str +
"\t轉(zhuǎn)回后數(shù)組長度:" + str.getBytes("UTF-8").length);
}
}
}
運(yùn)行結(jié)果為:
原數(shù)組長度:1 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
原數(shù)組長度:2 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
原數(shù)組長度:3 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
原數(shù)組長度:4 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
原數(shù)組長度:5 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
原數(shù)組長度:6 轉(zhuǎn)換為字符串:1 轉(zhuǎn)回后數(shù)組長度:1
唉~~,看來我得加強(qiáng)字符編碼方面的知識了!