用「講故事」的方式帶你認識Python編碼問題的起源和發展

佳雲大腦 發佈 2020-08-23T19:42:58+00:00

導讀:生動形象的告訴你Python編碼問題的起源與發展。我們在學習Python的過程中,可能會經常遇到下方這樣的編碼問題。

導讀:生動形象的告訴你Python編碼問題的起源與發展。

文 | 黃偉呢

來源:數據分析與統計學之美

問題起源

我們在學習Python的過程中,可能會經常遇到下方這樣的編碼問題。有時候我們需要選擇gbk,有時候需要選擇utf-8。你以為這樣就完了嗎?我們碰到的還有gb2312gb18030等各種奇奇怪怪的編碼。那麼,編碼的起源究竟是怎樣的呢?我們今天就用「講故事」的方式,帶你認識一下它。

黃同學給你講故事

1)烽火士兵的故事

在正式講故事之前,我們先來看一下下方這張圖,我們暫且稱其為《烽火士兵》的故事,那麼這個故事究竟是怎麼與編碼問題扯上聯繫的呢?接著聽我講故事。

這一串數字,從右朝左看

點燃第1根,代表有一個士兵,點燃第2根,代表有二個士兵。那麼也就是說,點燃2個烽火,最多可以表示三個士兵。梳理一下邏輯,1個烽火都不點,表示有零個士兵;只點燃第1個烽火,就表示有一個士兵;在點燃第2個烽火的時候,熄滅第1個烽火,就表示有二個士兵;同時點燃2個烽火,就表示有三個士兵。

綜上所述:2根烽火,可以表示:0、1、2、3個士兵,即1+2。3根烽火,可以表示:0、1、2、3、4、5、6、7個士兵,即1+2+4。依此類推下去…

通過上面的敘述,你可能已經發現了,這不就是類似計算機裡面的二進位數字嗎?只有0和1,0表示熄滅烽火,1表示點燃烽火。對應到計算機中就是,0表示關,1表示開。下面黃同學就帶著大家說一下「計算機中的0和1」

計算機的底層是電路,只認識0和1,就是你初中物理中所謂的「電路」,0表示關,1表示開,沒有別的玩意兒。但是你想呀,一個電路只有0和1的話,怎麼展示出這絢麗多彩的世界呢?因此,聰明的老外,把日常所用的文字和符號,編碼成0101010…類型,這樣電腦就能夠表示文字了。所以,先記住一個關鍵語:「用什麼編碼,就用什麼解碼」

由於,計算機是由美國人發明的。因此,最早的計算機編碼:ASCII碼(也是服務於美國人的),裡面只有美國人日常所用的26個英文字母、數字、標點等常用字符,所以,最早的計算機也只有英文、數字、標點等特殊字符。不要驚嘆為啥只有美國人常用的英文字母和符號,因為老美當時就沒有想過計算機會迅速在全世界普及開來,誰也不能提前預知未來。

接著我們來說說最早的計算機編碼:ASCII碼。ASCII碼占8個比特位,也就是一個字節,其中最前面一個位是擴展位,都是0,為了日後擴展所用,其餘位置不是0就是1。這是由於計算機對數字7不敏感,熟悉2、4、8、16、32...等數字,所以擴展了一位,成了8位。那麼根據排列組合的知識,ASCII碼可以表示2^7=128個碼位,即可以表示128種不同的符號,其實這些符號已經夠美國人使用了。這就是當時最早的計算機編碼(ASCII碼),這就是當時老美的打算。

2)計算機在中國的發展

隨著計算機在世界各地的發展,我們發現原有的碼位已經不夠存放多國的文字和符號了,為了講清楚這件事兒,我們以計算機在中國的發展為例,進行說明。

通過前面的敘述,我們已經知道最早的字符編碼ASCII碼中,並沒有中文,但是隨著計算機在中國的普及,我們必要要讓計算機中能夠表示中文呀,怎麼辦呢?基於此:中國北大方正團隊,發明了gbk編碼。但是這些字符肯定不能直接往ASCII碼裡面放,因為ASCII只有8位,最多才有2^8=256個空位,存放九萬多漢字,顯然不可能(就連中文中常用的3000漢字,也存放不了)。所以在gbk中,漢字用2個字節表示,變成了ASCII碼中字節長度的2倍,即gbk占16位,共2^16=65536個空位,這個對於存放常用漢字,多得多,但是,仍然不能將所有漢字存放進去,誰讓中華文化源遠流長,博大精深呢。

說到gbk,就不得不說它的兄弟姐妹了(如圖所示),其實它們是一個系列,都是由於當時的需要,逐步衍生出來的,這三種不同的編碼都是向上兼容的。可以看出:GB18030表示的字符數最多,這也就是為什麼有時候使用Python讀取Excel表時,使用GB2312和GBK都不行,而必須使用GB18030的原因了。

3)計算機如何兼容多國語言

計算機不僅在中國發展開來,其實計算機是在全世界迅速發展開來。如果說中國有自己獨有的GBK編碼,那麼韓國、日本肯定也有它們自己獨有的編碼。但是當今是「經濟全球化」的時代,任何一個國家都不可能的單獨發展,假如你有一個國際合作的業務,我們在中國寫的代碼,要是想拿到國外去用,出現亂碼,這樣多尷尬?那麼這個問題最終是怎麼解決的呢?

為此,美國人又發明了一個叫做「Unicode」的東西,又叫做「萬國碼」。其實完全可以見名知意,萬國碼萬國碼,肯定是為了包含全世界的字符編碼!那麼什麼是萬國碼呢?接著聽黃同學給你講。

我們知道:計算機擴展一般是成倍增加的,要麼是1個字節、2個字節、4個字節......。最開始的Unicode,又叫ucs-2,ASCII存儲採用1個字節,因此ucs-2採用2個字節進行存儲,最多有2^16=65536個空位,這樣仍然無法兼容全世界的字符。於是ucs-4產生了,存儲採用4個字節,共2^32=4億多個空位。但是據統計,全世界文字、數字、符號信息加起來也就23萬,對於4億多空間來說,ucs-4簡直太浪費空間了,這個對於文件傳輸來說,及其浪費流量。

考慮到節省空間,在Unicode基礎上,我們又發明了utf-8,一種可變長的Unicode字符編碼。Utf-8,對於英文來說,採用ASCII碼占位方式,占8位,即1個字節;存放歐洲文字時,占16位,即2個字節;存放中文時,占24位,即3個字節。雖然對於中文來說很浪費空間,但是為了能把全世界文字都統一起來,又為了節省空間,採用這種方式,已經很好了(因為畢竟不可能做到面面俱到,誰讓中國字符最多,會吃虧一點)。

編碼知識總結

1)字符編碼發展史

2)以小寫字母a為例,說明字符編碼

3)帶著大家寫寫代碼,認識一下字符編碼

① 關於Python2和Python3的區別

在Python2中,默認字符編碼是ASCII碼,因此在Python2中寫中文,首行一般都會加上-- coding:utf-8 --,看了這篇文章,我想你對這個東西已經有了一個清楚的認識。但是Python2現在已經停止更新了,我們了解即可,不用太關注。

對於Python3.x來說,默認字符編碼是utf-8,而utf-8是Unicode的擴展集。即Python3.x中默認所有的字符都是Unicode。說白點,我們在Python3.x中隨便寫點啥,編碼就是Unicode編碼。

對比Python2和Python3:

# 在Python2中如果要表示Unicode編碼,應該這樣寫。
my_name = u"黃偉"
# 在Python3中如果要表示Unicode編碼,應該這樣寫。
my_name = "黃偉"

說到這裡,我們可以下一個結論:不同編碼之間的轉換,都要經過一個Unicode

② encode編碼和decode解碼

>>> name1 = "我是你們的teacher老師"
>>> name2 = "你們是我的student學生"
>>> # 將name1編碼為「utf-8」
>>> name1_encode = name1.encode("utf-8")
>>> name1_encode
b'\xe6\x88\x91\xe6\x98\xaf\xe4\xbd\xa0\xe4\xbb\xac\xe7\x9a\x84teacher\xe8\x80\x81\xe5\xb8\x88'
>>> # 將name1_encode解碼還原
>>> name1_encode.decode("utf-8")
'我是你們的teacher老師'
---------------------------------------------------------
>>> # 將name2編碼為「gbk」
>>> name2_encode = name2.encode("gbk")
>>> name2_encode
b'\xc4\xe3\xc3\xc7\xca\xc7\xce\xd2\xb5\xc4student\xd1\xa7\xc9\xfa'
>>> # 將name2_encode解碼還原
>>> name2_encode.decode("gbk")
'你們是我的student學生'
-------------------------------------------------
>>> # name1_encode此時是「utf-8」編碼,如果用「gbk」解碼,會出現什麼?
>>> name1_encode.decode("gbk")
'鎴戞槸浣犱滑鐨則eacher鑰佸笀'
# 上面就是我們常說的亂碼、亂碼、亂碼!

代碼分析:從代碼中可以看出,如果是utf-8編碼,每個中文字符就是3個字節存儲。如果是gbk編碼,每個中文字符就是2個字節存儲。

關鍵字: