一文搞懂字符編碼問題,今後告別中文亂碼

在中文的語言環境裏,身爲程序員的咱們必定會遇到過中文亂碼的狀況,究其緣由就是字符編碼的問題。在沒有深刻理解其原理以前,會以爲中文編碼問題比較謎,莫名其妙地亂碼,又稀裏糊塗地好了。程序員

字符編碼是計算機技術的基石,本文但願幫助你們完全梳理清楚字符編碼問題,不只知其然,還知其因此然,擺脫被中文亂碼支配的感受。bash

在講解中文編碼問題以前,咱們須要先講講英語編碼,其解決方案是ASCII網絡

ASCII

ASCII的英文全稱是American Standard Code for Information Interchange,中文意思是美國信息交換標準代碼,是基於拉丁字母的一套計算機編碼系統,使用8位二進制表示字符。ui

在計算機內部,全部信息最終都是一個二進制值。每個二進制位(bit)有0和1兩種狀態,所以8個二進制位就能夠組合出256種狀態,這被稱爲一個字節(byte)。編碼

換句話說,一個字節能夠表示256種不一樣的狀態,每個狀態對應一個符號,也就是256個符號,從0000 00001111 1111,其數量計算公式:2^{8} = 256spa

列舉一部分ASCII碼錶,以下所示:操作系統

二進制 十六進制 圖形
0010 0000 20 (space)
0010 0001 21 !
0011 0001 31 1
0011 1101 3D =
0100 0100 41 A
0110 0001 61 a

英語是由26個基本拉丁字母、阿拉伯數字和英式標點符號組成,所以用128個符號就足夠l了,但ASCII碼對於其餘一些複雜的語言,就力不從心了,好比:漢字大約將近10萬個(雖然沒有準確的數字,但平常使用漢字也有幾千字)。3d

一個字節只能表示256種符號,確定是不夠的,就必須使用多個字節表達一個符號。爲了正確顯示中文字符,在1981年5月1日,由中國國家標準總局發佈了《信息交換用漢字編碼字符·基本集》,一般簡稱GBcode

GB類

中國大陸幾乎全部的中文系統和國際化的軟件都支持GB2312。GB2312是簡體中文常見的編碼方式,使用兩個字節表示一個漢字,因此最多能夠表示2^{8} \times 2^{8} = 65536個符號。orm

GB2312標準共收錄6763個漢字,其中一級漢字(經常使用字)3755個,二級漢字(較不經常使用)3008個,同時收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裏爾字母在內的682個字符。

GB2312基本知足了計算機處理簡體漢字的需求,所收錄的漢字覆蓋了99.75%的使用頻率,但對於罕見字和繁體字,GB2312就不能處理了。所以發明了後來的GBK和GB18030。

它們之間的關係以下圖所示:

GBK編碼是GB2312編碼的超集,向下徹底兼容GB2312,兼容的含義是不只字符兼容,並且相同字符的編碼也相同。而GB18030編碼向下兼容GBK和GB2312,GB18030編碼是變長編碼。

但不少像GB類的編碼方式都有一個共同的問題,容許計算機處理雙語環境,即拉丁字母和本地語言,卻沒法同時支持多語言環境,即多種語言混合的狀況。Unicode就是爲了解決這個問題而誕生的方案。

Unicode

世界上存在着多種語言,好比:西班牙語、韓語、俄語等等,它們也都分別有各自的編碼方式,因此同一個二進制數字能夠被解釋成不一樣的符號。若是想要正確的打開一個文本文件,就必須知道它的編碼方式,不然就會出現亂碼。

假若有一種編碼,將世界上全部的符號都歸入其中。每個符號都給予一個獨一無二的編碼,那麼亂碼問題就會消失。這就是Unicode,一種全部符號的編碼。

Unicode伴隨着通用字符集的標準而發展,當前最新的版本爲2019年5月公佈的12.1.0,已經收錄超過13萬個字符。Unicode涵蓋的數據除了視覺上的字形、編碼方式、標準的字符編碼外,還包含了字符特性,如大小寫字母。

然而,Unicode只是一個符號集,不表明計算機裏的編碼,它只規定了符號的二進制代碼,卻沒有規定這個二進制代碼應該如何存儲。

所以,致使了兩個問題:

  • 計算機怎麼知道三個字節表示一個符號,而不是分別表示三個符號呢?
  • 英文字母只用一個字節表示就夠了,但按照Unicode規定,每一個符號要用3個或4個字節表示,那麼英語文本的存儲空間將擴大3到4倍,是極大的浪費。

隨着互聯網的發展,不一樣國家的信息愈來愈多地在網絡中傳播,強烈須要一種統一的編碼方式,UTF-8就是在互聯網上被普遍使用的一種Unicode實現方式。

UTF-8

再次強調一下,UTF-8是Unicode的實現方式之一,並非惟一,也不等同於Unicode。除了UTF-8,還有UTF-16和UTF-32,只是不多被使用。

UTF-8的特色是對不一樣範圍的字符使用不一樣長度的編碼,它可使用1~4個字節表示一個符號,根據不一樣的符號而變化字節長度。

其編碼規則很簡單:

  • 對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的Unicode碼。所以對於英語字母,UTF-8編碼和ASCII碼是相同的。
  • 對於n字節的符號(n>1),第一個字節的前n位都設爲1,第n+1位設爲0,後面字節的前兩位一概設爲10,剩下的沒有說起的二進制位,所有爲這個符號的Unicode碼。

Unicode與UTF-8的字節對應關係:

Unicode範圍 UTF-8
U+0000~U+007F 0xxxxxxx
U+0080~U+07FF 110xxxxx 10xxxxxx
U+0800~U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
U+010000~U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

以「愛」爲例,其的Unicode是U+7231,是在U+0800~U+FFFF範圍內,因此採用3個字節進行編碼,其二進制爲01110010 00110001。

那麼用UTF-8表示,則以下圖所示:

後記

計算機操做系統中的編碼:

  • Windows下中文的默認編碼是GBK(GB2312)
  • Linux下中文的默認編碼是UTF-8

若是使用的是Linux系統,能夠經過以下命令,查看系統中文編碼:

echo $LANG
en_US.UTF-8
複製代碼

若是想要查看文件的原始編碼,而且轉換編碼,可使用enca命令,能夠經過apt-get install enca進行安裝。

enca -L zh_CN <file>  # 查看文件的編碼
enca -L zh_CN -x UTF-8 <file>  # 將文件編碼轉換爲UTF-8編碼
enca -L zh_CN -x UTF-8 <file_1> <file_2> # 保留原始文件
複製代碼

字符編碼選擇建議:

  1. 只有英文,選擇ASCII
  2. 主要存中文,對存儲大小比較敏感,選擇GB2312
  3. 通用性第一,處理簡單,選擇UTF-8

最後,安利你們一本我寫的掘金小冊《深刻理解NLP的中文分詞:從原理到實踐》,讓你從零開始掌握中文分詞技術,踏入NLP的大門。若是以上內容對你有所幫助,但願你可以點贊、轉發、評論,多謝多謝!

相關文章
相關標籤/搜索