字符集和编码
字符集和編碼--石鍋拌飯
?今天被一個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)中。每一個層面中擁有2
16=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 參考資料
- Python字符編碼具體解釋
- 阮一峰: 字符編碼筆記
- 知乎: Windows 記事本的 ANSI、Unicode、UTF-8 這三種編碼模式有什么差別?
- 關于字符編碼。你所須要知道的
總結
- 上一篇: 连接查询中的ON 子句和 WHERE 子
- 下一篇: ObjectOutputStream 和