用“講故事”的方式,帶你認識 Python 編碼問題的起源和發(fā)展!
閱讀本文大概需要 3 分鐘。
問題起源
我們在學習Python的過程中,可能會經(jīng)常遇到下方這樣的編碼問題。有時候我們需要選擇gbk,有時候需要選擇utf-8。你以為這樣就完了嗎?我們碰到的還有gb2312,gb18030等各種奇奇怪怪的編碼。那么,編碼的起源究竟是怎樣的呢?我們今天就用“講故事”的方式,帶你認識一下它。

故事環(huán)節(jié)1)烽火士兵的故事
在正式講故事之前,我們先來看一下下方這張圖,我們暫且稱其為《烽火士兵》的故事,那么這個故事究竟是怎么與編碼問題扯上聯(lián)系的呢?接著聽我講故事。

點燃第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。依此類推下去…

通過上面的敘述,你可能已經(jīng)發(fā)現(xiàn)了,這不就是類似計算機里面的二進制數(shù)字嗎?只有0和1,0表示熄滅烽火,1表示點燃烽火。對應到計算機中就是,0表示關,1表示開。下面黃同學就帶著大家說一下“計算機中的0和1”。
計算機的底層是電路,只認識0和1,就是你初中物理中所謂的“電路”,0表示關,1表示開,沒有別的玩意兒。但是你想呀,一個電路只有0和1的話,怎么展示出這絢麗多彩的世界呢?因此,聰明的老外,把日常所用的文字和符號,編碼成0101010…類型,這樣電腦就能夠表示文字了。所以,先記住一個關鍵語:“用什么編碼,就用什么解碼”。
由于,計算機是由美國人發(fā)明的。因此,最早的計算機編碼:ASCII碼(也是服務于美國人的),里面只有美國人日常所用的26個英文字母、數(shù)字、標點等常用字符,所以,最早的計算機也只有英文、數(shù)字、標點等特殊字符。不要驚嘆為啥只有美國人常用的英文字母和符號,因為老美當時就沒有想過計算機會迅速在全世界普及開來,誰也不能提前預知未來。
接著我們來說說最早的計算機編碼:ASCII碼。ASCII碼占8個比特位,也就是一個字節(jié),其中最前面一個位是擴展位,都是0,為了日后擴展所用,其余位置不是0就是1。這是由于計算機對數(shù)字7不敏感,熟悉2、4、8、16、32...等數(shù)字,所以擴展了一位,成了8位。那么根據(jù)排列組合的知識,ASCII碼可以表示2^7=128個碼位,即可以表示128種不同的符號,其實這些符號已經(jīng)夠美國人使用了。這就是當時最早的計算機編碼(ASCII碼),這就是當時老美的打算。

2)計算機在中國的發(fā)展
隨著計算機在世界各地的發(fā)展,我們發(fā)現(xiàn)原有的碼位已經(jīng)不夠存放多國的文字和符號了,為了講清楚這件事兒,我們以計算機在中國的發(fā)展為例,進行說明。
通過前面的敘述,我們已經(jīng)知道最早的字符編碼ASCII碼中,并沒有中文,但是隨著計算機在中國的普及,我們必要要讓計算機中能夠表示中文呀,怎么辦呢?基于此:中國北大方正團隊,發(fā)明了gbk編碼。但是這些字符肯定不能直接往ASCII碼里面放,因為ASCII只有8位,最多才有2^8=256個空位,存放九萬多漢字,顯然不可能(就連中文中常用的3000漢字,也存放不了)。所以在gbk中,漢字用2個字節(jié)表示,變成了ASCII碼中字節(jié)長度的2倍,即gbk占16位,共2^16=65536個空位,這個對于存放常用漢字,多得多,但是,仍然不能將所有漢字存放進去,誰讓中華文化源遠流長,博大精深呢。

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

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

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

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

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

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

3)帶著大家寫寫代碼,認識一下字符編碼
① 關于Python2和Python3的區(qū)別
在Python2中,默認字符編碼是ASCII碼,因此在Python2中寫中文,首行一般都會加上-- coding:utf-8 --,看了這篇文章,我想你對這個東西已經(jīng)有了一個清楚的認識。但是Python2現(xiàn)在已經(jīng)停止更新了,我們了解即可,不用太關注。
對于Python3.x來說,默認字符編碼是utf-8,而utf-8是Unicode的擴展集。即Python3.x中默認所有的字符都是Unicode。說白點,我們在Python3.x中隨便寫點啥,編碼就是Unicode編碼。
對比Python2和Python3:
#?在Python2中如果要表示Unicode編碼,應該這樣寫。
my_name?=?u"黃偉"
#?在Python3中如果要表示Unicode編碼,應該這樣寫。
my_name?=?"黃偉"說到這里,我們可以下一個結(jié)論:不同編碼之間的轉(zhuǎn)換,都要經(jīng)過一個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”解碼,會出現(xiàn)什么?
>>>?name1_encode.decode("gbk")
'鎴戞槸浣犱滑鐨則eacher鑰佸笀'
#?上面就是我們常說的亂碼、亂碼、亂碼!代碼分析:從代碼中可以看出,如果是utf-8編碼,每個中文字符就是3個字節(jié)存儲。如果是gbk編碼,每個中文字符就是2個字節(jié)存儲。
推薦閱讀
1
2
3
4
崔慶才
靜覓博客博主,《Python3網(wǎng)絡爬蟲開發(fā)實戰(zhàn)》作者
隱形字
個人公眾號:進擊的Coder
長按識別二維碼關注


