關於unicode,mbcs,utf8,charset,encoding等相關概念的說明

轉自:http://www.bloggern.com/2945.html 

    好像第一次遇到跟字符集有關的問題大概應該是在7年前,第一次寫java,總是會出現編碼轉換的問題,動不動就亂碼,基本上,本着實用主義的態度,以盲人摸象的手法,總算是能夠解決問題的。這些年來,不停的會遇到編碼方面的問題,隨着每一次解決問題,都感覺多揭開了一點籠罩在這一堆亂七八糟的東西上面的迷霧,然而,直到去年年中,我仍然沒有完全搞明白這堆亂七八糟的名詞和概念之間,究竟是怎樣的關係。 

    去年年中開始的項目,需要用c++來處理文檔,不可避免的遇到了編碼轉換的問題,在用c++處理的時候,我不得不仔細的探究在不同的編碼轉換的時候究竟發生了什麼事情,終於,總算是搞明白了這些東西。下面,我會試着解釋一下這些概念本身以及他們之間的關係,部分解釋來自維基百科或者msdn,版權不屬於我。 

    ANSI/ASCII : 由美國國家標準委員會指定的是基於拉丁字母的一套電腦編碼系統。它主要用於顯示現代英語和其他西歐語言。它是現今最通用的單字節編碼系統,並等同於國際標準ISO 646。(from wiki) 

    MBCS : 多字節字符集,是一種替代 Unicode 以支持無法用單字節表示的字符集(如日文和中文)的方法。爲國際市場編程時應考慮使用 Unicode 或 MBCS,或使程序能夠通過更改開關來生成支持兩種字符集之一的程序。最常見的 MBCS 實現是雙字節字符集 (DBCS)。一般來說,Visual C++(尤其是 MFC)完全支持 DBCS。(from msdn) 

    UNICODE : 是業界的一種標準,它可以使電腦得以呈現世界上數十種的文字系統。Unicode 是基於通用字元集(Universal Character Set)的標準來發展,並且同時也以書本的形式(The Unicode Standard,目前第五版由Addison-Wesley Professional出版,ISBN-10: 0321480910)對外發表。Unicode 包含了超過十萬個字元(在西元 2005 年, Unicode 的第十萬個字元被採納且認可成爲標準之一)、一組可用以作爲視覺參考的代碼圖表、一套編碼方法與一組標準字元編碼、一套包含了上標字、下標字等字元特性的列舉等。(from wiki) 

    UTF-8 : 英文全稱爲8-bit UCS/Unicode Transformation Format,是針對Unicode 的一種可變長度字元編碼。從名稱可以看出,UTF-8是專爲UCS/Unicode設計的傳輸格式。它可以用來表示 Unicode 標準中的任何字元,而且其編碼串流中的第一個位元組仍與 ASCII 兼容,令原來處理 ASCII 字符的軟件無需或只作少量改動後,便可繼續使用。因此,它逐漸成爲電子郵件、網頁及其他儲存或傳送文字的應用中,優先採用的編碼。(from wiki) 

    基本的名詞解釋完了,現在來仔細的解釋一下Charset/Encoding(字符集/編碼)。Charset很容易和Encoding搞混,也是剛開始接觸字符編碼問題是最容易被暈掉的概念。字符集的概念,實際上,包含兩個方面,一個,是字符的集合,即所謂的Charset,一個是編碼方案,也就是所謂的Encoding。所謂字符的集合,意即一個字符集,定義了它所包含的所有符號,這實際上正是字符集名字的真正含義。也就是說,狹義上的字符集,並不包含編碼方案,它僅僅是定義了哪些符號屬於這個字符集。但是,通常來說,一個字符集並不僅僅定義字符集合,同時,它還爲每個符號定義一個二進制編碼,所以,當我們提到GB2312的時候,我們並不僅僅是指GB2312字符集,同時,也指明瞭編碼方案是GB2312,即Charset= GB2312,Encoding=GB2312,這說明,我們的文檔中不包含GB2312以外的字符,而它們的二進制編碼採用GB2312規定的編碼方式。簡單的情況的確如此,字符集等於編碼,編碼等於字符集。 

    但是,通常把我們搞暈的,正是一個例外,Unicode。Unicode字符集本身定義的編碼方案通常稱爲UCS-2,或者一個更通用的名字, UTF-16。然而,由於UTF-16不能和現行的基於ascii的編碼方案兼容,比較重點的問題在於0x0,在基於ascii的編碼方案中,一個8位的 0x0總是表示一個字符串的結束的,而UTF-16則不然,它的一個字符,完全有可能在高8位或者低8位上等於0x0,這會導致很多應用程序錯誤,尤其是在網絡傳輸協議當中可能導致大量的字符串錯誤截斷。於是,有了UTF-8,UTF-8提供了一個跟ascii兼容的unicode字符集編碼方案。網絡上常見的說法說UTF-8是1到3位變長編碼,這是錯誤的,UTF-8是1到6位變長編碼,3位的說法來源於大多數常用漢字被包括在3位編碼的範疇以內,而另外,從現行的Unicode規範來講,UTF-8實際上是1到4位的編碼,因爲再加上兩位編碼所擴展的範圍現在Unicode還沒有定義任何字符。 UTF-8的編碼方案首先保證跟127個標準ascii字符兼容,也就是說,在UTF-8方案下,Unicode的0x000000–0x00007F範圍的字符被表達爲0x0-0x7F的一個字節的二進制編碼。其次,UTF-8保證,所有0x7F以上的字符,在被轉譯成多字節字符時,每個字節的最高位一定爲1,這實際上也是大多數MBCS方案的基本原則,否則應用程序沒法識別多字節字符的字節組合方式或者出現錯誤的0x0。問題在於,一般的DBCS雙字節方案可以簡單的根據高位是否爲1而判定單字節還是雙字節,而UTF-8是變長的,應用程序需要知道如何組合連續的字節數據,按照UTF-8的規定,除了最高的一個字節外,其餘的所有字節均以10開頭,而最高字節的開頭,110表示連續2位,1110表示連續3位,11110表示連續4位。(一個具體字符的UTF-8編碼值根據一個簡單的對應算法從UTF-16得到,這裏就不詳細講述,請自行google)。因此,我們必須明確一個概念,UTF-8是 unicode字符集的一個編碼方案,當我們在說到UTF-8字符和Unicode字符的時候,在某些情況下,它們在邏輯上是等價的,但是,他們並不是同一個東西,因爲Unicode字符在二進制上還有一個選擇就是原生的UTF-16編碼。 

    總結一下,Unicode字符集規定的標準編碼方案是UCS-2(UTF-16),用兩個字節表示一個Unicode字符,而事實上,UCS-4 (UTF-32)也已經被提出了,用4個字節表示一個Unicode字符,然後,一個常用的Unicode編碼方案—UTF-8,它用1到4(6)位的變長字節來表示一個Unicode字符,並可以從一個簡單的轉換算法從UTF-16直接得到。這三個編碼方案(Encoding)都對應於Unicode字符集(Charset)。 

    然後,需要解釋一下Codepage-代碼頁,codepage實際上是一張表,通常的codepage是一個從unicode到其他mbcs的轉換索引表,比如windows上常用的MS936代碼頁,實際上就是GB2312到unicode的轉換表,我們知道,windows是完全基於 unicode的,MS的應用程序也大多是基於unicode開發的,他們對GB2312的支持,正是來源於codpage932,通過cp932的轉換,應用程序可以在unicode和gb2312之間來回轉換。需要多一句嘴的是關於日文編碼,IBM和微軟都提供了幾個工業上常用的日文編碼的代碼頁,比如對於shift-jis,IBM的代碼頁是CCSID943,這也是java在轉換時使用的代碼頁,而MS的代碼頁是MS936,他們兩者都是針對 shift-jis的代碼頁,算是兩個並行的工業標準,在某些字符上轉換結果並不一致,這可能會導致應用程序錯誤,比如我遇到的典型問題是通過java程序將shift-jis數據轉換爲unicode後傳輸給VC編寫的程序再轉換爲shift-jis,由於在兩次轉換中使用了不兼容的代碼頁,導致數據錯誤。這並沒有什麼特別的解決辦法,只能是查找特殊字符列表作特殊處理。 

    另外一個問題,就是所謂的基於unicode的應用程序,在應用程序內部字符串究竟以什麼形式表達,一般來說,是以UCS-2也就是UTF-16 的二進制形式來表達的,UTF-8一般只是作爲數據傳輸格式和文檔保存格式。具體一點,對於java代碼,javac編譯程序在你沒有指定源文件編碼的情況下,總是將其認爲是本地缺省編碼,比如在簡體中文windows上會被認識爲GB2312,而在日文windows上會被識別爲shift-jis,然後實際上,在編譯的時候,gb或者jis字符都會被轉換成對應的UCS-2二進制值。而對於C++程序,當你簡單的用 ”中” 這樣的引號引用的形式的時候,編譯器會簡單的將其識別爲本地編碼並且在編譯後的二進制代碼中被表達爲本地編碼的二進制值(我不知道c++編譯器能否指定源文件編碼。。。我不太熟悉c++),但是,如果你用 L”中” 這樣的標準c++的unicode字符定義方式來定義字符時,將會發生和javac編譯程序一樣的事情,編譯程序會主動的將字符正確的以本地編碼識別並轉換成UCS-2值保存爲編譯後的二進制代碼。C++程序中,如果以L宏定義字符串,可以在程序中直接得到Unicode值,即UTF-16編碼值,但在 java中,實際上我們並不能直接得到字符串的二進制值,不過可以通過String的getbytes方法指定UTF-16編碼得到。至於各種編碼之間的轉換,這裏就不贅述了。 

    最後,解釋一下關於font的問題,其實,很多時候我們遇到的亂碼並沒有出現編碼轉換錯誤,只是由於應用程序指定的字體沒有包含對應的字符圖像而已,這種情況,通常我們可以看到有部分字符是可以正常顯示的。Font文件實際上也是一個索引文件,一個encoding索引加上位圖。現在的字體文件通常包含兩個編碼索引,一個unicode,一個該字體對應語言的常用編碼,應用程序在顯示字符的時候,通過查找對應的索引而得到字符的位圖。 
發佈了12 篇原創文章 · 獲贊 19 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章