好像第一次遇到跟字符集有關的問題大概應該是在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,一個該字體對應語言的經常使用編碼,應用程序在顯示字符的時候,經過查找對應的索引而獲得字符的位圖。