??? 今天在做ajax的portlet的時候用response返回數(shù)據(jù)遇到了中文亂碼的問題,給我們帶來了很大的麻煩。unicode、utf8、gb2312、gbk之間關(guān)系如何,互有什么淵源,對于unicode來說在將來不遠(yuǎn)的幾年里, 它已經(jīng)很接近于取代 ASCII
與 Latin-1 編碼的位置了.
它不僅允許你處理處理事實上存在于地球上的任何語言文字,
而且提供了一個全面的數(shù)學(xué)與技術(shù)符號集,
因此可以簡化科學(xué)信息交換。于是痛下決心好好研究研究研究這些編碼之間的關(guān)系,以便以后能夠比較輕松的解決之類的問題。經(jīng)過在網(wǎng)上查詢資料研究得出如下關(guān)于utf-8與unicode編碼的心得:
??? 最開始美國是用Ascii來保存英文字母和一些字符(空格,標(biāo)點符號等等)的。
??? 后來中國人們得到計算機(jī)時,已經(jīng)沒有可以利用的字節(jié)狀態(tài)來表示漢字,況且有6000多個常用漢字需要保存呢。我們就用兩個字節(jié)來表示漢字信息這樣我們就可以組合出大約7000多個簡體漢字了。在這些編碼里,我們還把數(shù)學(xué)符號、羅馬希臘的
字母、日文的假名們都編進(jìn)去了,連在 ASCII
里本來就有的數(shù)字、標(biāo)點、字母都統(tǒng)統(tǒng)重新編了兩個字節(jié)長的編碼,這就是常說的"全角"字符,而原來在127號以下的那些就叫"半角"字符了。這就是GB2312。
??? 再后來,我們把第一個字節(jié)是大于127就固定表示這是一個漢字的開始。結(jié)果擴(kuò)展之后的編碼方案被稱為 GBK 標(biāo)準(zhǔn),GBK 包括了 GB2312 的所有內(nèi)容,同時又增加了近20000個新的漢字(包括繁體字)和符號。后來少數(shù)民族也要用電腦了,于是我們再擴(kuò)展,又加了幾千個新的少數(shù)民族的字,GBK 擴(kuò)成了 GB18030。
??? Unicode 只是分配整數(shù)給字符的編碼表,但是UNICODE 在制訂時沒有考慮與任何一種現(xiàn)有的編碼方案保持兼容,這使得 GBK 與UNICODE 在漢字的內(nèi)碼編排上完全是不一樣的,沒有一種簡單的算術(shù)方法可以把文本內(nèi)容從UNICODE編碼和另一種編碼進(jìn)行轉(zhuǎn)換,這種轉(zhuǎn)換必須通過查表來進(jìn)行。UNICODE
是用兩個字節(jié)來表示為一個字符,他總共可以組合出65535不同的字符,這大概已經(jīng)可以覆蓋世界上所有文化的符號。如果還不夠也沒有關(guān)系,ISO已經(jīng)準(zhǔn)備
了UCS-4方案,說簡單了就是四個字節(jié)來表示一個字符,這樣我們就可以組合出21億個不同的字符出來(最高位有其他用途).
??? UNICODE 來到時,一起到來的還有計算機(jī)網(wǎng)絡(luò)的興起,UNICODE 如何在網(wǎng)絡(luò)上傳輸也是一個必須考慮的問題,于是面向傳輸?shù)谋姸?
UTF(UCS Transfer
Format)標(biāo)準(zhǔn)出現(xiàn)了,顧名思義,UTF8就是每次8個位傳輸數(shù)據(jù),而UTF16就是每次16個位,只不過為了傳輸時的可靠性,從UNICODE到
UTF時并不是直接的對應(yīng),而是要過一些算法和規(guī)則來轉(zhuǎn)換。這就是我們應(yīng)用諸如tomcat的服務(wù)器其中的配置文件會有編碼的設(shè)置,一般的默認(rèn)設(shè)置就是utf8。
??? 在網(wǎng)絡(luò)里傳遞信息時有一個很重要的問題,就是對于數(shù)據(jù)高低位的解讀方式,一些計算機(jī)是采用低位先發(fā)送的方法,例如我們PC機(jī)采用的 x86 架構(gòu),而另一些是采用高位先發(fā)送的方式,在網(wǎng)絡(luò)中交換數(shù)據(jù)時,為了核對雙方對于高低位的認(rèn)識是否是一致的,采用了一種很簡便的方法,就是在文本流的開始時
向?qū)Ψ桨l(fā)送一個標(biāo)志符——如果之后的文本是高位在位,那就發(fā)送"FEFF",反之,則發(fā)送"FFFE"。
??? 講到這里,我們再順便說說一個很著名的奇怪現(xiàn)象:當(dāng)你在 windows 的記事本里新建一個文件,輸入"聯(lián)通"兩個字之后,保存,關(guān)閉,然后再次打開,你會發(fā)現(xiàn)這兩個字已經(jīng)消失了,代之的是幾個亂碼!呵呵......其實這是因為GB2312編碼與UTF8編碼產(chǎn)生了編碼沖撞的原因。
??? utf8 與 unicode 的轉(zhuǎn)換標(biāo)準(zhǔn)如下:
Unicode
UTF-8
0000 - 007F
0xxxxxxx
0080 - 07FF
110xxxxx 10xxxxxx
0800 - FFFF
1110xxxx 10xxxxxx 10xxxxxx
如:中醫(yī)藥的 unicode 碼為:\u4e2d\u533b\u836f,去掉 \u 后為:? 4e2d? 533b? 836f
這三個字的二進(jìn)制編碼分別為:
0100? 1110? 00 10? 1101
0101? 0011? 00 11? 1011
1000? 0011? 01 10? 1111? 他們都是大于0800小于ffff,所以適用第三種模板
將他們分別編碼為:
1110 0100 10 111000 10 101101?? ---> ??? e4b8ad
1110 0101 10 001100 10 111011?? --->???? e58cbb
1110 1000 10 001101 10 101111?? --->???? e88daf
e4b8ad、e58cbb、e88daf分別為:中醫(yī)藥 的utf8編碼
??? 而當(dāng)你新建一個文本文件時,記事本的編碼默認(rèn)是ANSI, 如果你在ANSI的編碼輸入漢字,那么他實際就是GB系列的編碼方式,在這種編碼下,"聯(lián)通"的內(nèi)碼是: \u8054\u901a
c1 1100 0001
aa 1010 1010
cd 1100 1101
a8 1010 1000
注
意到了嗎?第一二個字節(jié)、第三四個字節(jié)的起始部分的都是"110"和"10",正好與UTF8規(guī)則里的兩字節(jié)模板是一致的,于是再次打開記事本時,記事本
就誤認(rèn)為這是一個UTF8編碼的文件,讓我們把第一個字節(jié)的110和第二個字節(jié)的10去掉,我們就得到了"00001
101010",再把各位對齊,補(bǔ)上前導(dǎo)的0,就得到了"0000 0000 0110
1010",不好意思,這是UNICODE的006A,也就是小寫的字母"j",而之后的兩字節(jié)用UTF8解碼之后是0368,這個字符什么也不是。這就
是只有"聯(lián)通"兩個字的文件沒有辦法在記事本里正常顯示的原因。
??? 而如果你在"聯(lián)通"之后多輸入幾個字,其他的字的編碼不見得又恰好是110和10開始的字節(jié),這樣再次打開時,記事本就不會堅持這是一個utf8編碼的文件,而會用ANSI的方式解讀之,這時亂碼又不出現(xiàn)了。
而如果你在"聯(lián)通"之后多輸入幾個字,其他的字的編碼不見得又恰好是110和10開始的字節(jié),這樣再次打開時,記事本就不會堅持這是一個utf8編碼的文件,而會用ANSI的方式解讀之,這時亂碼又不出現(xiàn)了。 ?