字符集和編碼——Unicode(UTF&UCS)深度歷險

  計算機網絡誕生後,大家慢慢地發現一個問題:一個字節放不下一個字符了!因爲需要交流,本地化的文字需要能夠被支持。

  最初的字符集使用7bit來存儲字符,因爲那時只需要存下一些英文字母和符號。後來雖然擴展到使用8bit來存儲一個字符了(這種方式被國際標準化組織收錄,成爲ISO8859-1。在字符集發展歷程中國際標準化組織一直髮揮着重要作用。),也還是無法存儲諸如中文的字符。

  混亂的年代到來了。爲了存儲下自己的文字,各個國家和地區(多爲非拉丁語系的民族,因爲這些語種字符數很龐大)各自使用兩個字節即16bit來存放一個字符。他們把首字節的前2^7個位留給一個字節能存下的字符(如英文字母和標點符號),而後的位和後面的字節一起組成適用於本地文字的字符。這中方式一直沿用至今,如GB2312、GBK(此編碼爲微軟爲簡體中文用戶設計的)、GB18030、BIG5等。使用這種方式有一個問題:不同的數值(假如我們把字符換算成數字)在不同的字符集可能有不同的意義,甚至使用不同字體也會呈現出不同的效果!而且從一個字符集到另一個字符集的轉化也會非常麻煩!

  標準化一直在進行。爲了解決上述麻煩,各種機構都做出了不同的努力。以微軟爲代表的操作系統可能更多的是提供用戶可選擇的語言和區域設置,並使用如CodePage(代碼頁,Windows操作系統對不同地區不同字符集的支持方式。如GBK爲CP936)來隔離差異,國際標準化組織(ISO)編纂了ISO10646來規範和整合字符集(被成爲通用字符集 Universal Character Set,UCS )。統一碼聯盟(由各個大型企業及組織共同維護)發佈了統一碼(Unicode)項目。

  起初,UCS和Unicode各自爲政,但1991年前後他們都發現:世界不需要兩個不一樣的“統一”、“通用”的字符集。所以他們聯合起來維護一個字符集,現在他們的差別大概是發佈新版本時使用什麼字體了-_-。

  UCS和Unicode都使用最大32bit來存儲字符,他們(其實是一樣的,不過還是區分一下)的碼位(字符數)有1114112個,從0x0到0x0x10FFFF。

  大家可能會奇怪,32bit最多可以表示超42億個字符(即從0x0到0xFFFFFFFF),爲什麼只使用了其中這麼小一部分呢?其實,這裏面還有一些其他原因。

 

  使用32bit來存儲字符看起來是一件一勞永逸的方式,但如果這32bit是定寬的(即任何字符都要使用完這32bit)的話就不可避免的造成空間的浪費,程序效率也會降低!

  能不能把UCS(Unicode)設計成“變寬”的呢?聰明的設計師想到了一個主意,他們發明了一種名爲“統一碼轉換格式”即UTF的來將字符對應的數字(可能從小於127至大於100萬不等)轉化爲多個字節來進行存儲。

  簡單說來,UCS或Unicode只是定義了從0到1114112這些數字各自是什麼字符(而指示界面上該怎麼顯示這個字符則是由“字體”來管理,比如Windows下“微軟雅黑”字體就是這個樣子的(部分):

  

)。而如果從1~4個字節(變寬)還原出這個數字(或字符)就是UTF的事兒了。

  比如“漢”字,對應數字爲23383,那麼要使用3字節的UTF8格式字節進行存儲(原因我們下面再講),或者1字節的UTF16或1字節的UTF32。

  “漢” UTF8 = {0xE6, 0xB1, 0x89}

    UTF16 = {0x6c49}

    UTF32 = {0x6c49}(高位補0可省略)

  Unicode字符集的劃分大概有兩種:按“單元(Cell)”劃分(Unicode官方文檔是這樣分的)和按“平面(Plane)”劃分。一個單元爲128個字符,一個平面有65536個字符。

  我們通常使用的一個平面取值爲0x0到0xFFFF,這個平面被成爲BMP(Basic Multilingual Plane)即“平面0”。由於這個平面只是用兩個字節就可以完整表示,字符集又可以成爲UCS-2(即使用2字符的“通用字符集”,UCS-4即爲4字節,將UCS-4的高兩位去除即爲UCS-2)。

  我們常用的27973個漢字都存放與平面0上,整個Unicode共定義了71226個漢字(Unicode5.0.0),平面2的43253個字符都是漢字。

  

  UTF之間的關係和轉換。上文說道,“漢”字需要3個UTF8字節來存儲,這是因爲要符合UTF格式的規範。

  一個模版可以告訴我們UTF8是怎樣存儲數據的:

Unicode編碼(16進制) 
UTF-8 字節流(二進制)
000000 - 00007F
0xxxxxxx
000080 - 0007FF
110xxxxx 10xxxxxx
000800 - 00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

  可以看到,如果使用兩個字節來存儲數據,UTF8最多可以存儲2^11個字符,最大的數字爲0x7FF即2047,顯然無法存下數字爲23383的“漢”字。

  而通過模版我們也可以看出Unicode的最大bit數爲21。

 

  數字向UTF的轉換也很簡單了,把數字換成二進制(不足21位的高位補0),然後填入對應UTF的模版(UTF16和UTF32的模版大家請自行查看,LE和BE區別在於高位和低位的位置,Windows和Linux爲LE,MacOS爲BE)中替換xxxxxxx就行了!很簡單吧。

  UTF在文件中的存儲。UTF格式在文件中總有固定文件頭:

UTF編碼
Byte Order Mark
UTF-8
EF BB BF
UTF-16LE
FF FE
UTF-16BE
FE FF
UTF-32LE
FF FE 00 00
UTF-32BE
00 00 FE FF

 

  如“漢”字在文件中的存儲(不包括頭):

Unicode編碼
UTF-16LE 
UTF-16BE 
UTF32-LE 
UTF32-BE
0x006C49
49 6C
6C 49
49 6C 00 00
00 00 6C 49

  

  各個系統和語言對Unicode的支持:

    Windows NT從底層支持Unicode(不幸的是,Windows 98只是小部分支援Unicode)。先天即被ANSI束縛的C程序設計語言通過對寬字元集的支持來支持Unicode。

    Windows底層使用UTF16,Linux使用UTF32(未考證)。

    C#和Java支持UTF16且是默認行爲(如字符串天生爲UTF16格式字符數組,Java還可以使用'\uxxxx'格式聲明一個字符)。

    XML及其子集HTML對UTF16支持很好,爲跨平臺你可以使用'&#xxxx;'來聲明一個字符。

 

 

 歡迎您移步我們的交流羣,無聊的時候大家一起打發時間:Programmer Union

 或者通過QQ與我聯繫:點擊這裏給我發消息

 (最後編輯時間2013-09-17 20:59:38)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章