深入淺出地理解python中的編碼

2021-08-29 02:57:53 字數 3406 閱讀 7119

python處理文字的功能非常強大,但是如果是初學者,沒有搞清楚python中的編碼機制,也經常會遇到亂碼或者decode error。本文的目的是簡明扼要地說明python的編碼機制,並給出一些建議。

問題是我們的靶子,心中沒有問題去學習就會抓不住重點。

本文使用的程式設計環境是centos6.7,python2.7。我們在shell中鍵入python以開啟python命令列,並鍵入如下兩句話:

s = "中國zg"

e = s.encode("utf-8")

現在的問題是:這段**能執行嗎?

答案是不能,會報如下的錯:

unicodedecodeerror: 'ascii' codec can't decode byte0xe4in position 0: ordinal not in range(128)

請留意一下錯誤中說明的0xe4,它是我們分析錯誤的突破口。

相信很多人都遇到過這個錯誤。那麼新的問題來了。

要搞清楚原因,我們不妨認真分析下這兩句話的執行流程:

首先,我們通過鍵盤在python命令列直譯器中鍵入了中國zg並且給它加上了英文的雙引號,然後又賦值給了變數s,看起來很稀鬆平常是不是?其實裡面大有玄機。

當我們通過鍵盤在程式中輸入字元時,我們是通過作業系統完成這個功能的。我們在螢幕上看到的中國zg實際上是作業系統給我們人類的乙個反饋,告訴你:「嗨,哥們,你在程式中輸入了字元中國zg

那作業系統給程式的反饋是什麼呢?答案就是01串,這個01串是什麼樣子,又是怎麼生成的呢?

答案就是作業系統使用自己的預設編碼方式,將中國zg進行了編碼,並把編碼後的01串給了程式。

我們用的centos系統預設的編碼是utf-8,所以,只要知道中國zg每個字元的utf-8的編碼就可以知道01串是什麼了。

查詢後,可以獲得它們的編碼是(以16進製制和2進製表示):中國

zge4b8ad

e59bbd

7a67

11100101 10011011 10111101

11100101 10011011 10111101

01111010

01100111

現在我們知道作業系統傳給程式的01串長什麼樣子了。然後,程式會怎麼處理它呢?

程式看到這個01串被雙引號包圍著,自然知道這個01串是乙個字串。然後這個字串被賦值給了s。

到此,就是第一句的執行邏輯。

現在繼續進行第二句的執行。

e = s.encode("utf-8")的意思是將字串s用utf-8進行編碼,並將編碼後的字串賦值給e。問題來了,程式現在知道s中的01串,還知道這個01串表示的是字串,但這個字串的編碼是什麼呢?作業系統只給程式傳來了01串,並沒有告訴程式這個01串用的字元編碼是什麼。

此時,python程式就會用它自己預設的編碼當作s的編碼,進而來識別s中的內容。這個預設的編碼是ascii,所以,它會用ascii來解釋這個01串,識別出字串的內容,再將這個字串轉為utf-8編碼。

好了,程式碰到的第乙個位元組就是e4(11100101 ),傻眼! ascii編碼中沒有這玩意兒啊。ascii編碼中位元組第一位都是0啊。

怎麼辦?

報錯唄,於是我們就看到了上面的錯誤。

錯誤中的0xe4就是字元 中 的utf8編碼的第乙個位元組。

知道問題出在**了,怎麼解決這個問題呢?

顯然,我們只要告訴程式,這個s中的01串的編碼是utf-8,程式就應該能正確工作。

但這樣的解決方法有乙個問題,就是不夠通用。

假如我有個程式,它要讀取很多文字檔案,每個文字檔案的編碼都不一樣,豈不是針對每個讀進來的檔案都維護乙個編碼資訊?很繁瑣。

進一步,如果這些文字檔案的內容還要做相互的比較連線之類的操作,編碼都不一致,豈不是更麻煩?

python是怎麼聰明地解決這個問題的呢?

很簡單,就是decode!

decode的意思是說,你有乙個字串,並且你知道它的編碼,只要你用該編碼decode這個字串,那麼,python就會識別出裡面的字元內容,同時,建乙個int陣列,將每個字元的unicode序號存進去。

所有的字串都這樣做,就可以確保在程式執行過程中,各種**獲得的字串都有一樣的表示。它們就可以方便地進行各種操作了。

上面說的 int陣列會被python封裝成乙個物件,即unicode物件。

下面,我們在python命令列中輸入如下兩行**:

e = s.decode("utf-8")

isinstance(e,unicode)

程式的輸出是true,這說明,decode後返回的e確實是乙個unicode物件。

unicode在這裡是乙個類,是python裡面的類。

e 被稱作unicode字串,意思是說,它存的是字元的unicode序號,並沒有使用任何編碼。

然後,我們就可以將e編碼成任意一種編碼,比如下面的操作都是可以的

e.encode("utf-8")

e.encode("gbk")

只要你選擇的編碼能夠對e中的字元進行編碼即可,如果不能編碼,就會報錯。

比如,如果你嘗試這樣:

e.encode("ascii")
由於ascii並不能編碼 中國 這兩個字元,所以會爆出 encode error。

至此,我們已經看到了兩種錯誤,decode error 和encode error,並解決了它們。

首先,這樣的處理方法非常的簡單。任何文字,只要它進入程式時進行一次decode,就會變成unicode物件,裡面用int存著每個字元的unicode序號。只要在這個文字要輸出時再進行一次encode,編碼成我們需要的編碼就可以了。

問題是,所有的字元都用乙個int來表示會不會太浪費空間?畢竟,用ascii編碼,英文的字元只要乙個位元組就可以了。

確實會費點空間,但是現在的記憶體都足夠大,而且我們只在程式內部使用這種方式,當字串要寫入檔案或者通過網路傳輸時,我們都會進行相應的編碼的。

還有乙個問題,那些寫死在程式中的字串怎麼辦?難道每次使用都要進行一次decode?不同的作業系統預設使用的編碼是不一樣的,當我們在linux下,通常需要用utf8做decode,在windows下,通常需要用gbk做 decode。這樣,我們的**就只能在特定的平台執行。

python給我們提供了乙個很簡單的辦法,只要在字串前面加乙個u,它就會幫我們探測系統的編碼,並自動完成decode。

本文用乙個很常見的錯誤為起點,詳細分析了python中的編碼問題。我們看到了python處理字元問題的簡單之處,也能夠理解為什麼python有這麼強大的文字處理功能。

**     

深入淺出地,徹徹底底地理解python中的編碼

python處理文字的功能非常強大,但是如果是初學者,沒有搞清楚python中的編碼機制,也經常會遇到亂碼或者decode error。本文的目的是簡明扼要地說明python的編碼機制,並給出一些建議。問題1 問題在 問題是我們的靶子,心中沒有問題去學習就會抓不住重點。本文使用的程式設計環境是cen...

深入淺出理解索引

一 深入淺出理解索引結構 實際上,您可以把索引理解為一種特殊的目錄。sql server提供了兩種索引 聚集索引 clustered index,也稱聚類索引 簇集索引 和非聚集索引 nonclustered index,也稱非聚類索引 非簇集索引 下面,我們舉例來說明一下聚集索引和非聚集索引的區別...

深入淺出理解索引

一 深入淺出理解索引結構 實際上,您可以把索引理解為一種特殊的目錄。sql server提供了兩種索引 聚集索引 clustered index,也稱聚類索引 簇集索引 和非聚集索引 nonclustered index,也稱非聚類索引 非簇集索引 下面,我們舉例來說明一下聚集索引和非聚集索引的區別...