深入淺出地,徹徹底底地理解python中的編碼
邀請關注微信公眾號:人工智慧LeadAI(ID:atleadai)
歡迎訪問我們的官方主頁:www.leadai.org
python處理文本的功能非常強大,但是如果是初學者,沒有搞清楚python中的編碼機制,也經常會遇到亂碼或者decode error。本文的目的是簡明扼要地說明python的編碼機制,並給出一些建議。
問題1:問題在哪裡?
問題是我們的靶子,心中沒有問題去學習就會抓不住重點。
本文使用的編程環境是centos6.7,python2.7。我們在shell中鍵入python以打開python命令行,並鍵入如下兩句話:
s = "中國zg"n e = s.encode("utf-8")n
現在的問題是:這段代碼能運行嗎?
答案是不能,會報如下的錯:
UnicodeDecodeError: ascii codec cant decode byte 0xe4 in position 0: ordinal not in range(128)n
請留意一下錯誤中說明的0xe4,它是我們分析錯誤的突破口。
相信很多人都遇到過這個錯誤。那麼新的問題來了。
問題2:why?
要搞清楚原因,我們不妨認真分析下這兩句話的執行流程:
首先,我們通過鍵盤在python命令行解釋器中鍵入了 中國zg 並且給它加上了英文的雙引號,然後又賦值給了變數s,看起來很稀鬆平常是不是?其實裡面大有玄機。
當我們通過鍵盤在程序中輸入字元時,我們是通過操作系統完成這個功能的。我們在屏幕上看到的 中國zg 實際上是操作系統給我們人類的一個反饋,告訴你:「嗨,哥們,你在程序中輸入了字元 中國zg 」
那操作系統給程序的反饋是什麼呢?答案就是01串,這個01串是什麼樣子,又是怎麼生成的呢?
答案就是操作系統使用自己的默認編碼方式,將中國zg進行了編碼,並把編碼後的01串給了程序。
我們用的centos系統默認的編碼是utf-8,所以,只要知道中國zg每個字元的utf-8的編碼就可以知道01串是什麼了。
查詢後,可以獲得它們的編碼是(以16進位和2進位表示):
| 中 |國 | z |g|
|: ------------- |:-------------:| -----:|
|E4B8AD | E59BBD|7A|67|
|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串的現有編碼才能解析出裡面的字元,也才能用新的編碼方式,如utf-8來重新編碼它。操作系統只給程序傳來了01串,並沒有告訴程序這個01串用的字元編碼是什麼。
此時,python程序就會用它自己默認的編碼當作s的編碼,進而來識別s中的內容。這個默認的編碼是ASCII,所以,它會用ASCII來解釋這個01串,識別出字元串的內容,再將這個字元串轉為utf-8編碼。
好了,程序碰到的第一個位元組就是E4(11100101 ),傻眼! ASCII編碼中沒有這玩意兒,因為ASCII編碼中位元組第一位都是0。
怎麼辦?
報錯唄,於是我們就看到了上面的錯誤。
錯誤中的0xe4就是字元 「中」 的utf8編碼的第一個位元組。
問題3:How?
知道問題出在哪裡了,怎麼解決這個問題呢?
顯然,我們只要告訴程序,這個s中的01串的編碼是utf-8,程序就應該能正確工作。
但這樣的解決方法有一個問題,就是不夠通用。
假如我有個程序,它要讀取很多文本文件,每個文本文件的編碼都不一樣,豈不是針對每個讀進來的文件都維護一個編碼信息?很繁瑣。
進一步,如果這些文本文件的內容還要做相互的比較連接之類的操作,編碼都不一致,豈不是更麻煩?
python是怎麼聰明地解決這個問題的呢?
很簡單,就是decode!
decode的意思是說,你有一個字元串,並且你知道它的編碼,只要你用該編碼decode這個字元串,那麼,python就會識別出裡面的字元內容,同時,建一個int數組,將每個字元的unicode序號存進去。
所有的字元串都這樣做,就可以確保在程序運行過程中,各種來源獲得的字元串都有一樣的表示。它們就可以方便地進行各種操作了。
上面說的 int數組會被python封裝成一個對象,即unicode對象。
問題4:如何搞定
下面,我們在python命令行中輸入如下兩行代碼:
e = s.decode("utf-8")nisinstance(e,unicode)n
程序的輸出是True,這說明,decode後返回的e確實是一個unicode對象。
unicode在這裡是一個類,是python裡面的類。
e 被稱作unicode字元串,意思是說,它存的是字元的unicode序號,並沒有使用任何編碼。
然後,我們就可以將e編碼成任意一種編碼,比如下面的操作都是可以的。
e.encode("utf-8") e.encode("gbk")n
只要你選擇的編碼能夠對e中的字元進行編碼即可,如果不能編碼,就會報錯。
比如,如果你嘗試這樣:
e.encode("ascii")n
由於ASCII並不能編碼 中國 這兩個字元,所以會爆出 encode error。
至此,我們已經看到了兩種錯誤,decode error 和encode error,並解決了它們。
問題5:如何評價python的這種字元編碼處理方法?
首先,這樣的處理方法非常的簡單。任何文本,只要它進入程序時進行一次decode,就會變成unicode對象,裡面用int存著每個字元的unicode序號。只要在這個文本要輸出時再進行一次encode,編碼成我們需要的編碼就可以了。
問題是,所有的字元都用一個int來表示會不會太浪費空間?畢竟,用ASCII編碼,英文的字元只要一個位元組就可以了。
確實會費點空間,但是現在的內存都足夠大,而且我們只在程序內部使用這種方式,當字元串要寫入文件或者通過網路傳輸時,我們都會進行相應的編碼的。
還有一個問題,那些寫死在程序中的字元串怎麼辦?難道每次使用都要進行一次decode?不同的操作系統默認使用的編碼是不一樣的,當我們在linux下,通常需要用utf8做decode,在Windows下,通常需要用gbk做 decode。這樣,我們的代碼就只能在特定的平台運行。
python給我們提供了一個很簡單的辦法,只要在字元串前面加一個u,它就會幫我們探測系統的編碼,並自動完成decode。
問題6:總結下,學到了什麼
本文用一個很常見的錯誤為起點,詳細分析了python中的編碼問題。我們看到了python處理字元問題的簡單之處,也能夠理解為什麼python有這麼強大的文本處理功能。
測試題:看你是否真正理解了。
假設一台linux上有一個文件a.txt,裡面的內容是"中文"兩個字元,編碼方式是utf-8。
現在,在python程序中寫如下語句:
import codecns=""nwith codec.open("a.txt",encoding="utf-8") as f:ns=f.readline().strip()nwith open("b.txt","w") as f:nf.write(s)n
請問這段代碼能執行嗎?為什麼?
答案:不能!
s底下的表示是unicode,寫出時python會對其進行編碼,默認用的ascii編碼無法對"中文"兩個字元進行編碼,所以會報錯!
原文鏈接:http://www.jianshu.com/p/eb22cee6c553
查閱更為簡潔方便的分類文章以及最新的課程、產品信息,請移步至全新呈現的www.leadai.org
關注人工智慧LeadAI公眾號,查看更多專業文章
http://weixin.qq.com/r/ZDnC2j-E5GKbrXu592x2 (二維碼自動識別)
大家都在看
LSTM模型在問答系統中的應用
基於TensorFlow的神經網路解決用戶流失概覽問題
最全常見演算法工程師面試題目整理(一)
最全常見演算法工程師面試題目整理(二)
TensorFlow從1到2 | 第三章 深度學習革命的開端:卷積神經網路
裝飾器 | Python高級編程
今天不如來複習下Python基礎
推薦閱讀:
TAG:Python |