字符集和編碼

字符集和編碼--石鍋拌飯

 今天被一個python編碼問題折騰了半下午,編碼問題一直是個讓人比較糾結的問題,寫這篇文章想簡單的總結下python中一些常見的編碼問題以及解決方案。這是第一篇,先總結下字符集和編碼的一些基本概念和內容。


在編程中常常可以見到各種字符集和編碼,包括ASCII,MBCS,Unicode等字符集。確切的說,其實字符集和編碼是兩個不同概念,只是有些地方有重合罷了。對於ASCII,MBCS等字符集,基本上一個字符集方案只採用一種編碼方案,而對於Unicode,字符集和編碼方案是明確區分的。那麼先有幾個術語需要說明下,下面這段術語說明摘抄自伯樂在線《關於字符編碼,你所需要知道的知識》:

  • 字符集(Character Set):顧名思義,就是字符的集合。如ASCII字符集,定義了128個字符,而gb2312定義了7445個字符。計算機中字符集的嚴格定義來說指的是已編號的字符的有序集合(不一定連續)。
  • 字符碼(Code Point):指的就是字符集中每個字符的數字編號。例如ASCII字符集用0-127這連續的128個數字分別表示128個字符;GBK字符集使用區位碼的方式爲每個字符編號,首先定義一個94X94的矩陣,行稱爲“區”,列稱爲“位”,然後將所有國標漢字放入矩陣當中,這樣每個漢字就可以用唯一的“區位”碼來標識了。例如“中”字被放到54區第48位,因此字符碼就是5448。而Unicode中將字符集按照一定的類別劃分到0~16這17個層面(Planes)中,每個層面中擁有216=65536個字符碼,因此Unicode總共擁有的字符碼,也即是Unicode的字符空間總共有17*65536=1114112。
  • 字符編碼:將字符集中的字符碼映射爲字節流的一種具體實現方案。例如ASCII字符編碼規定使用單字節中低位的7個比特去編碼所有的字符。例如‘A’的編號是65,用單字節表示就是0×41,因此寫入存儲設備的時候就是b’01000001’。GBK編碼則是將區位碼(GBK的字符碼)中的區碼和位碼的分別加上0xA0(160)的偏移(之所以要加上這樣的偏移,主要是爲了和ASCII碼兼容),例如剛剛提到的“中”字,區位碼是5448,十六進制是0×3630,區碼和位碼分別加上0xA0的偏移之後就得到0xD6D0,這就是“中”字的GBK編碼結果。
  • 代碼頁(Code Page)一種字符編碼具體形式。早期字符相對少,因此通常會使用類似表格的形式將字符直接映射爲字節流,然後通過查表的方式來實現字符的編解碼。現代操作系統沿用了這種方式。例如Windows使用936代碼頁、Mac系統使用EUC-CN代碼頁實現GBK字符集的編碼,名字雖然不一樣,但對於同一漢字的編碼肯定是一樣的。


1 ASCII

其中ASCII標準本身就規定了字符和字符編碼方式,採用單字節編碼,總共可以編碼128個字符,如空格的編碼是32,小寫字母a是97,所以ASCII既是字符集又是編碼方案。


2 MBCS

對於英文來說,128個符號編碼已經夠用了,然而對於其他語言比如中文,顯然就不夠了。因此就出現了多字節字符集MBCS(Multi-Byte Character Set)。如GB2312,GBK,GB18030,BIG5等編碼都屬於MBCS。由於MBCS大都使用2個字節編碼,所以有時候也叫DBCS(Double-Byte Character Set)。我們在Linux系統中看到含有中文的文件編碼常常是CP936,那這個其實就是GBK編碼了,這個名字的由來是因爲IBM曾經發明瞭一個Code Page的概念,把這些多字節編碼收入其中,GBK編碼正好位於936頁,所以就簡稱CP936了。


3 Unicode

而後大家覺得各種編碼太多不方便,不如所有語言字符都使用一套字符集來表示,於是就出現了Unicode。Unicode/UCS(Unicode Character Set)標準只是一個字符集標準,但是它並沒有規定字符的存儲和傳輸方式。Unicode是一種字符集而不是具體的編碼,它主要有3種編碼方式:最初Unicode標準使用2個字節表示一個字符,編碼方案是UTF-16,還有使用4個字節表示一個字符的編碼方案UTF-32。而後來使用英文字符的國家覺得不好,原來一個字符存儲的現在變成了2個字符,空間增大了一倍,由此UTF-8編碼。UTF-8編碼中,英文佔一個字節,中文佔3個字節。


如上面所提到的,Unicode字符集主要採用UTF-8,UTF-16等方式進行編碼存儲,當然,gbk等字符編碼也可以編碼Unicode所有的字符集,也算是Unicode的一種字符編碼。那麼這樣的話,計算機如何知道文件採用哪種方式編碼呢?Unicode規範中又定義,在每個文件最前面加入一個表示編碼順序的字符BOM(Byte Order Mark)。比如石鍋拌飯中的“石”的UTF-16編碼是77F3,採用UTF-16方式存儲使用2個字節,一個字節是77,一個字節是F3.存儲的時候如果77在前面,F3在後面,則稱爲big endian方式。反之,則是Little endian方式。,這個字符正好也是2個字節,爲FEFF。如果一個文本文件頭兩個字節威FEFF,則表示採用Big endian方式編碼;否則就是Little endian方式。而UTF-8的BOM是EFBBBF,總結如下:

BOM_UTF8 '\xEF\xBB\xBF' 
BOM_UTF16_LE '\xFF\xFE' 
BOM_UTF16_BE '\xFE\xFF'


並不是所有的編輯器都會寫入BOM,但即使沒有BOM,Unicode還是可以讀取的,只是需要指定編碼,不然可能會失效。


4 ANSI

此外還有一種不得不提的是ANSI,ANSI在windows系統中極爲常見,其實ANSI是Windows code pages,這個模式根據當前的locale選定具體編碼,如果系統locale是簡體中文則採用GBK編碼,繁體中文爲BIG5編碼,日文則是JIS編碼。


此外windows中喜歡把BOM_UTF16_LE編碼稱作Unicode,把BOM_UTF8稱作UTF-8。也有人說UTF-8不需要BOM來標示,其實是不多的,這是因爲編輯器一般默認使用UTF-8來測試字符編碼而已,如果可以成功解碼,就用UTF-8進行解碼。即便最開始採用的是ANSI保存的,打開文件時還是最先使用UTF-8來解碼。比如你用windows的記事本程序新建一個文件,寫入“奼塧”並用ANSI編碼保存,再次打開文件,會發現“奼塧”會變成“漢a”。


5 實例分析

還是以石鍋拌飯的“石”字來看看在windows下面各種編碼方式下的編碼吧。打開windows的記事本程序,分別用ANSI,Unicode(實際是BOM_UTF16_LE),Unicode Big endian,UTF-8這幾種編碼方式看看最終是否跟之前分析的一樣。這裏使用UltraEdit來查看16進制編碼,可以打開“編輯”-》16進制編輯功能來查看。


ANSI編碼保存,編碼是CA AF。這也表示GBK編碼存儲也採用了Big endian方式。

Unicode編碼保存,編碼是FF FE F3 77。 

Unicode Big endian編碼保存,編碼是 FE FF 77 F3。 

UTF-8編碼保存,編碼是EF BB BF E7 9F B3。


6 參考資料

發佈了121 篇原創文章 · 獲贊 133 · 訪問量 68萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章