如果你開發(fā)過的軟件項(xiàng)目中涉及到多語言支持的問題,那么相信你沒少碰到過亂碼問題,然后在尋求解決問題的途徑過程中被一些概念如ASCII, ISO-8859-1, Unicode,UTF-8,GBK,GB2312等等所困擾。本文有助于你正確的理解這些概念。 1. ASCII 用7位編碼將英文字符和一些常用的符號(hào)存諸為從0到127的數(shù)值。 2. ISO-8859-1 法語、西班牙語和德語之類的西歐語言都使用叫做ISO-8859-1的編碼系統(tǒng)(也叫做“latin-1”)。它使用7位ASCII字符表示從0到127的字符,但接著擴(kuò)展到了128-255的范圍來表示如n上帶有一個(gè)波浪線(241),和u上帶有兩個(gè)點(diǎn)(252)的字符等等??梢哉fASCII是ISO-8859-1的子集。 3. Unicode Unicode用一個(gè)2字節(jié)數(shù)字表示每個(gè)字符,從0到65535。每個(gè) 2 字節(jié)數(shù)字表示至少在一種世界語言中使用的一個(gè)唯一字符。(在多種語言中都使用的字符具有相同的數(shù)字碼。)這樣就確保每個(gè)字符一個(gè)數(shù)字,并且每個(gè)數(shù)字一個(gè)字符。Unicode數(shù)據(jù)永遠(yuǎn)不會(huì)模棱兩可。Unicode使用相同的數(shù)字表示ASCII和ISO-8859-1中的字符。只是這兩種編碼用一個(gè)字節(jié)表示,而Unicode用兩個(gè)字節(jié)表示。所以Unicode表示這兩種編碼的字符時(shí)只要用低字節(jié)就可以了,高字節(jié)為0。 4. UTF-8 UTF-8是一種變長(zhǎng)的編碼方式,每個(gè)UTF-8的編碼可以是1至6個(gè)字節(jié)長(zhǎng)。它將Unicode編碼的字符采用變長(zhǎng)的方式進(jìn)行編碼。對(duì)Unicode中屬于ISO-8859-1的編碼采用和ISO-8859-1相同的單字節(jié)編碼。其他字符采用兩字節(jié)以上的編碼。實(shí)際上對(duì)于兩個(gè)字節(jié)的Unicode編碼,UTF-8只要三個(gè)字節(jié)即可表示。第一個(gè)字節(jié)由n個(gè)1(1< n <= 6)開始, n表示編碼的字節(jié)數(shù),后面每個(gè)字節(jié)都以10開始,后面6位為有效位。將第一位的剩余位和后面的所有字節(jié)的后六位連接起來就是對(duì)應(yīng)的Unicode編碼的數(shù)值。例如漢字“中”的編碼: Unicode: 4E 2D 01001110 00101101 UTF-8: E4 B8 AD 11100100 10111000 10101101 可以通過以下方式進(jìn)行證實(shí): 用記事本創(chuàng)建一個(gè)文本文件,輸入漢字“中”分別保存為Unicode格式和UTF-8格式。 將UltraEdit的自動(dòng)識(shí)別UTF-8文件格式選項(xiàng)禁止,然后用其打開這兩個(gè)文件,選用二進(jìn)制查看方式,可以看到:
UTF-8格式文件編碼為“EF BB BF E4 B8 AD”。其中有個(gè)三字節(jié)的前綴“EF BB BF”,這是UTF-8格式文本文件的標(biāo)識(shí)。不過這個(gè)前綴不要,某些文本查看軟件也可以通過編碼判斷出UTF-8格式。“E4 B8 AD”就是“中”的UTF-8編碼。
Unicode格式的文件的完整編碼是“FF FE 2D 4E”。前面有個(gè)雙字節(jié)前綴“FF FE”,這是Unicode格式文本文檔的編碼標(biāo)識(shí)。而我們看到的編碼是“2D 4E”,而不是如我前面所說的“4E 2D”,為什么呢?因?yàn)閿?shù)字是按照低字節(jié)在先高字節(jié)在后的順序存儲(chǔ)的,所以實(shí)際的Unicode編碼恰恰是“4E2D”。 5. GB2312和GBK 這兩種編碼都是漢字的編碼標(biāo)準(zhǔn),其中前者是后者的子集。GBK編碼的文本文檔中對(duì)于ASCII中的字符用相同的單字節(jié)表示;對(duì)于漢字和漢語中的標(biāo)點(diǎn)符號(hào)等采用雙字節(jié)編碼,其中高字節(jié)大于0x80,而ASCII的所有字符的編碼均小于0x80, 所以ASCII和GBK的字符是可以混和起來的。GBK的字符集和Unicode進(jìn)行轉(zhuǎn)換是無規(guī)則的,需要轉(zhuǎn)換表才能進(jìn)行轉(zhuǎn)換。 6.下面給出我用Java語言寫的Unicode和UTF-8轉(zhuǎn)換的程序片段,供大家參考。 因?yàn)?/span>Java語言的字符使用Unicode編碼的,所以程序中是對(duì)UTF-8編碼的字符串的字節(jié)數(shù)組和Java的String類型進(jìn)行轉(zhuǎn)換。String對(duì)象中的每一個(gè)Character就是一個(gè)Unicode編碼的字符。 public String utf8Bytes2String(byte[] buff){ if(buff == null) return null;
StringBuffer sb = new StringBuffer();
int idx = 0;
if(buff[0] == (byte)0xEF && buff[1] == (byte)0xBB && buff[2] == (byte)0xBF) idx = 3;//Skip UTF8 header while(idx < buff.length){ int hB = buff[idx] & 0xFF; int bCnt = 0; int check = 0x80; for(int i=0; i<8; i++){ if((hB & check) != 0){ bCnt ++; check >>= 1; }else break; }
if(bCnt <= 1){ char c = 0; c |= buff[idx] & 0xFF; sb.append(c); idx++; }else if(bCnt == 2){ char c = 0; c |= buff[idx] & 0x03; c <<= 6; if((buff[idx+1] & 0xC0) != 0x80) return null; c |= buff[idx+1] & 0x idx += 2; sb.append(c); }else if(bCnt == 3){ char c = 0; c |= buff[idx] & 0x c <<= 6; if((buff[idx+1] & 0xC0) != 0x80) return null; c |= buff[idx+1] & 0x c <<= 6; if((buff[idx+2] & 0xC0) != 0x80) return null; c |= buff[idx+2] & 0x idx += 3; sb.append(c); }else return null; }
return sb.toString(); }
public byte[] string2Utf8Bytes(String str){ if(str == null) return null;
ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { string2Utf8Stream(str, bos); } catch (IOException e) { e.printStackTrace(); } return bos.toByteArray(); } public void string2Utf8Stream(String str, OutputStream os) throws IOException { if(str == null || os == null) return;
for(int i=0; i<str.length(); i++){ char c = str.charAt(i); if(c < 0x80){ os.write((byte)c); }else if(c >=0x80 && c < 0x100){ int hi = c >> 6; hi |= 0xC0; int lo = c & 0x lo |= 0x80; os.write(hi); os.write(lo); }else{ int first = c >> 12; first |= 0xE0; int second = c >> 6; second &= 0x second |= 0x80; int third = c & 0x third |= 0x80; os.write(first); os.write(second); os.write(third); } } } |
|