python處理文本的功能很是強大,可是若是是初學者,沒有搞清楚python中的編碼機制,也常常會遇到亂碼或者decode error。本文的目的是簡明扼要地說明python的編碼機制,並給出一些建議。python
問題是咱們的靶子,心中沒有問題去學習就會抓不住重點。
本文使用的編程環境是centos6.7,python2.7。咱們在shell中鍵入python以打開python命令行,並鍵入以下兩句話:linux
如今的問題是:這段代碼能運行嗎?
答案是不能,會報以下的錯:shell
請留意一下錯誤中說明的0xe4,它是咱們分析錯誤的突破口。編程
相信不少人都遇到過這個錯誤。那麼新的問題來了。centos
要搞清楚緣由,咱們不妨認真分析下這兩句話的執行流程:
首先,咱們經過鍵盤在python命令行解釋器中鍵入了 中國zg 而且給它加上了英文的雙引號,而後又賦值給了變量s,看起來很稀鬆日常是否是?其實裏面大有玄機。數組
當咱們經過鍵盤在程序中輸入字符時,咱們是經過操做系統完成這個功能的。咱們在屏幕上看到的 中國zg 其實是操做系統給咱們人類的一個反饋,告訴你:「嗨,哥們,你在程序中輸入了字符 中國zg 」網絡
那操做系統給程序的反饋是什麼呢?答案就是01串,這個01串是什麼樣子,又是怎麼生成的呢?
答案就是操做系統使用本身的默認編碼方式,將中國zg進行了編碼,並把編碼後的01串給了程序。app
咱們用的centos系統默認的編碼是utf-8,因此,只要知道中國zg每一個字符的utf-8的編碼就能夠知道01串是什麼了。python2.7
查詢後,能夠得到它們的編碼是(以16進制和2進製表示):學習
如今咱們知道操做系統傳給程序的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編碼的第一個字節。
知道問題出在哪裏了,怎麼解決這個問題呢?
顯然,咱們只要告訴程序,這個s中的01串的編碼是utf-8,程序就應該能正確工做。
但這樣的解決方法有一個問題,就是不夠通用。
假如我有個程序,它要讀取不少文本文件,每一個文本文件的編碼都不同,豈不是針對每一個讀進來的文件都維護一個編碼信息?很繁瑣。
進一步,若是這些文本文件的內容還要作相互的比較鏈接之類的操做,編碼都不一致,豈不是更麻煩?
python是怎麼聰明地解決這個問題的呢?
很簡單,就是decode!
decode的意思是說,你有一個字符串,而且你知道它的編碼,只要你用該編碼decode這個字符串,那麼,python就會識別出裏面的字符內容,同時,建一個int數組,將每一個字符的unicode序號存進去。
全部的字符串都這樣作,就能夠確保在程序運行過程當中,各類來源得到的字符串都有同樣的表示。它們就能夠方便地進行各類操做了。
上面說的 int數組會被python封裝成一個對象,即unicode對象。
下面,咱們在python命令行中輸入以下兩行代碼:
程序的輸出是True,這說明,decode後返回的e確實是一個unicode對象。
unicode在這裏是一個類,是python裏面的類。
e 被稱做unicode字符串,意思是說,它存的是字符的unicode序號,並無使用任何編碼。
而後,咱們就能夠將e編碼成任意一種編碼,好比下面的操做都是能夠的
只要你選擇的編碼可以對e中的字符進行編碼便可,若是不能編碼,就會報錯。
好比,若是你嘗試這樣:
至此,咱們已經看到了兩種錯誤,decode error 和encode error,並解決了它們。
首先,這樣的處理方法很是的簡單。任何文本,只要它進入程序時進行一次decode,就會變成unicode對象,裏面用int存着每一個字符的unicode序號。只要在這個文本要輸出時再進行一次encode,編碼成咱們須要的編碼就能夠了。
問題是,全部的字符都用一個int來表示會不會太浪費空間?畢竟,用ASCII編碼,英文的字符只要一個字節就能夠了。
確實會費點空間,可是如今的內存都足夠大,並且咱們只在程序內部使用這種方式,當字符串要寫入文件或者經過網絡傳輸時,咱們都會進行相應的編碼的。
還有一個問題,那些寫死在程序中的字符串怎麼辦?難道每次使用都要進行一次decode?不一樣的操做系統默認使用的編碼是不同的,當咱們在linux下,一般須要用utf8作decode,在Windows下,一般須要用gbk作 decode。這樣,咱們的代碼就只能在特定的平臺運行。
python給咱們提供了一個很簡單的辦法,只要在字符串前面加一個u,它就會幫咱們探測系統的編碼,並自動完成decode。
本文用一個很常見的錯誤爲起點,詳細分析了python中的編碼問題。咱們看到了python處理字符問題的簡單之處,也可以理解爲何python有這麼強大的文本處理功能。
假設一臺linux上有一個文件a.txt,裏面的內容是"中文"兩個字符,編碼方式是utf-8。
如今,在python程序中寫以下語句:
請問這段代碼能執行嗎?爲何?
答案:不能!
s底下的表示是unicode,寫出時python會對其進行編碼,默認用的ascii編碼沒法對"中文"兩個字符進行編碼,因此會報錯!
做者:milter
連接:https://www.jianshu.com/p/eb22cee6c553