鍍金池/ 教程/ Java/ 編碼詳情
編碼詳情
java編碼轉(zhuǎn)換過程
認識字符集
javaWeb中的編碼解碼
認識字符集
總結(jié)
java是如何編碼解碼的
解決URL中文亂碼問題
JSP頁面編碼過程

編碼詳情

隨著計算機的發(fā)展、普及,世界各國為了適應本國的語言和字符都會自己設(shè)計一套自己的編碼風格,正是由于這種亂,導致存在很多種編碼方式,以至于同一個二進制數(shù)字可能會被解釋成不同的符號。為了解決這種不兼容的問題,偉大的創(chuàng)想Unicode編碼應時而生??!

Unicode

Unicode又稱為統(tǒng)一碼、萬國碼、單一碼,它是為了解決傳統(tǒng)的字符編碼方案的局限而產(chǎn)生的,它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉(zhuǎn)換、處理的要求??梢韵胂骍nicode作為一個“字符大容器”,它將世界上所有的符號都包含其中,并且每一個符號都有自己獨一無二的編碼,這樣就從根本上解決了亂碼的問題。所以Unicode是一種所有符號的編碼[2]。

Unicode伴隨著通用字符集的標準而發(fā)展,同時也以書本的形式對外發(fā)表,它是業(yè)界的標準,對世界上大部分的文字系統(tǒng)進行了整理、編碼,使得電腦可以用更為簡單的方式來呈現(xiàn)和處理文字。Unicode至今仍在不斷增修,迄今而至已收入超過十萬個字符,它備受業(yè)界認可,并廣泛地應用于電腦軟件的國際化與本地化過程。

我們知道Unicode是為了解決傳統(tǒng)的字符編碼方案的局限而產(chǎn)生的,對于傳統(tǒng)的編碼方式而言,他們都存在一個共同的問題:無法支持多語言環(huán)境,這對于互聯(lián)網(wǎng)這個開放的環(huán)境是不允許的。而目前幾乎所有的電腦系統(tǒng)都支持基本拉丁字母,并各自支持不同的其他編碼方式。Unicode為了和它們相互兼容,其首256字符保留給ISO 8859-1所定義的字符,使既有的西歐語系文字的轉(zhuǎn)換不需特別考量;并且把大量相同的字符重復編到不同的字符碼中去,使得舊有紛雜的編碼方式得以和Unicode編碼間互相直接轉(zhuǎn)換,而不會丟失任何信息[1]。

實現(xiàn)方式

一個字符的Unicode編碼是確定的,但是在實際傳輸過程中,由于不同系統(tǒng)平臺的設(shè)計不一定一致,以及出于節(jié)省空間的目的,對Unicode編碼的實現(xiàn)方式有所不同。Unicode的實現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format,簡稱為UTF)[1]。

Unicode是字符集,它主要有UTF-8、UTF-16、UTF-32三種實現(xiàn)方式。由于UTF-8是目前主流的實現(xiàn)方式,UTF-16、UTF-32相對而言使用較少,所以下面就主要介紹UTF-8。

UCS

提到Unicode可能有必要了解下,UCS。UCS(Universal Character Set,通用字符集),是由ISO制定的ISO 10646(或稱ISO/IEC 10646)標準所定義的標準字符集。它包括了其他所有字符集,保證了與其他字符集的雙向兼容,即,如果你將任何文本字符串翻譯到UCS格式,然后再翻譯回原編碼,你不會丟失任何信息。

UCS不僅給每個字符分配一個代碼,而且賦予了一個正式的名字。表示一個UCS或Unicode值的十六進制數(shù)通常在前面加上“U+”,例如“U+0041”代表字符“A”。

Little endian & Big endian

由于各個系統(tǒng)平臺的設(shè)計不同,可能會導致某些平臺對字符的理解不同(比如字節(jié)順序的理解)。這時將會導致同意字節(jié)流可能會被解釋為不同的內(nèi)容。如某個字符的十六進制為4E59,拆分為4E、59,在MAC上讀取時是歐諾個低位開始的,那么MAC在遇到該字節(jié)流時會被解析為594E,找到的字符為“奎”,但是在Windows平臺是從高字節(jié)開始讀取,為4E59,找到的字符為“乙”。也就是說在Windows平臺保存的“乙”跑到MAC平臺上就變成了“奎”。這樣勢必會引起混亂,于是在Unicode編碼中采用了大頭(Big endian)、小頭(Little endian)兩種方式來進行區(qū)分。即第一個字節(jié)在前,就是大頭方式,第二個字節(jié)在前就是小頭方式。那么這個時候就出現(xiàn)了一個問題:計算機怎么知道某個文件到底是采用哪種編碼方式的呢?

Unicode規(guī)范中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做”零寬度非換行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節(jié),而且FF比FE大1。

如果一個文本文件的頭兩個字節(jié)是FE FF,就表示該文件采用大頭方式;如果頭兩個字節(jié)是FF FE,就表示該文件采用小頭方式。

UTF-8

UTF-8是一種針對Unicode的可變長度字符編碼,可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度。它可以用來表示Unicode標準中的任何字符,且其編碼中的第一個字節(jié)仍與ASCII兼容,這使得原來處理ASCII字符的系統(tǒng)無須或只須做少部份修改,即可繼續(xù)使用。因此,它逐漸成為電子郵件、網(wǎng)頁及其他存儲或傳送文字的應用中,優(yōu)先采用的編碼。

UTF-8使用一到四個字節(jié)為每個字符編碼,編碼規(guī)則如下:

1)對于單字節(jié)的符號,字節(jié)的第一位設(shè)為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。

2)對于n字節(jié)的符號(n>1),第一個字節(jié)的前n位都設(shè)為1,第n+1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。

轉(zhuǎn)換表如下:

Unicode UTF-8
0000 ~007F 0XXX XXXX
0080 ~07FF 110X XXXX 10XX XXXX
0800 ~FFFF 1110XXXX 10XX XXXX 10XX XXXX
1 0000 ~1F FFFF 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX
20 0000 ~3FF FFFF 1111 10XX 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX
400 0000 ~7FFF FFFF 1111 110X 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX 10XX XXXX

根據(jù)上面的轉(zhuǎn)換表,理解UTF-8的轉(zhuǎn)換編碼規(guī)則就變得非常簡單了:第一個字節(jié)的第一位如果為0,則表示這個字節(jié)單獨就是一個字符;如果為1,連續(xù)多少個1就表示該字符占有多少個字節(jié)。

以漢字”嚴”為例,演示如何實現(xiàn)UTF-8編碼[3]。

已知”嚴”的unicode是4E25(100111000100101),根據(jù)上表,可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800-0000 FFFF),因此”嚴”的UTF-8編碼需要三個字節(jié),即格式是”1110xxxx 10xxxxxx 10xxxxxx”。然后,從”嚴”的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了,”嚴”的UTF-8編碼是”11100100 10111000 10100101″,轉(zhuǎn)換成十六進制就是E4B8A5。

Unicode與UTF-8之間的轉(zhuǎn)換

通過上面的例子我們可以看到”嚴”的Unicode碼為4E25,UTF-8編碼為E4B8A5,他們兩者是不一樣的,需要通過程序的轉(zhuǎn)換來實現(xiàn),在Window平臺最簡單的直觀的方法就是記事本。

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/3.1.png" alt="" />

在最下面的”編碼(E)”處有四個選項:ANSI、Unicode、Unicode big endian、UTF-8。

ANSI:記事本的默認的編碼方式,對于英文文件是ASCII編碼,對于簡體中文文件是GB2312編碼。注意:不同 ANSI 編碼之間互不兼容,當信息在國際間交流時,無法將屬于兩種語言的文字,存儲在同一段 ANSI 編碼的文本中

Unicode:UCS-2編碼方式,即直接用兩個字節(jié)存入字符的Unicode碼。該方式是”小頭”little endian方式。

Unicode big endian:UCS-2編碼方式,”大頭”方式。

UTF-8:閱讀上面(UTF-8)。

實例:在記事本中輸入”嚴”字,依次選擇ANSI、Unicode、Unicode big endian、UTF-8四種編碼風格,然后另存為,使用EditPlus文本工具使用”16進制查看器”進行查看,得到如下結(jié)果:

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/3.2.png" alt="" />

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/3.3.png" alt="" />

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/3.4.png" alt="" />

http://wiki.jikexueyuan.com/project/java-chinese-garbled-solution/images/3.5.png" alt="" />

ANSI:兩個字節(jié)”D1 CF”正是”嚴”的GB2312編碼。

Unicode:四個字節(jié)”FF FE 25 4E”,其中”FF FE”表示小頭存儲方式,真正的編碼為”25 4E”。

Unicode big endian:四個字節(jié)”FE FF 4E 25″,”FE FF”表示大頭存儲方式,真正編碼為”4E 25″。

UTF-8:編碼是六個字節(jié)”EF BB BF E4 B8 A5″,前三個字節(jié)”EF BB BF”表示這是UTF-8編碼,后三個”E4B8A5″就是”嚴”的具體編碼,它的存儲順序與編碼順序是一致的。

參考文獻&更多閱讀

1、Unicode維基百科:http://zh.wikipedia.org/wiki/Unicode

2、Unicode百度百科:http://baike.baidu.com/view/40801.htm

3、字符編碼筆記:ASCII,Unicode和UTF-8:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

4、UTF-8百度百科:http://baike.baidu.com/view/25412.htm