有個特別好玩的現象,當咱們爲了python編碼頭疼的時候,幾乎搜索到全部的文章都會先發一通牢騷。而後在迫不得已地寫解決思路(是解決思路不是方案)。這個問題真不是新手問題,即便是十幾年python老手也常常頭疼。中國外國都同樣。看看這個python專家在 PyCon大會上用半個多小時講解亂碼的視頻就瞭解了,他本身都給本身的來回encoding, decoding, encoding, decoding說暈了,臺下舉手他都拒絕回答,可想而知這個問題複雜性。
我認爲,幾乎每一個pythoner,都會有一段人生浪費在了編碼上。能夠說這個問題,是若是你不不折不扣解決,就永遠會崩潰的地步。翻看我曾經寫的數篇文章就知道了:html
牢騷結束,下面是我又一次用了兩個成天才測試整理書寫完成的ipython notebook筆記。ipynb
格式的筆記源文件在這裏,固然有可能會連接失效,有喜歡ipython的live coding筆記的且想要用這個筆記測試編碼的,請聯繫我。python
print大法
若是python的print的特性都沒有了解的話,但願你不要貿然嘗試用print去調試測試亂碼編碼的問題。
這裏的print厲害到讓你不高興的地步——它無論你塞過來的是什麼格式什麼編碼,字符串數組對象什麼的的都一口氣全打印出來。
感受好像很好,但實際上是咱們仔細研究編碼問題的最大阻礙。
由於你塞給print一個unicode它能打出中文,塞一個utf-8或iso8895給它,也同樣給你打印出原文。這樣以來,你看着它出現原文後,就欣喜若狂產生了一種勝利的錯覺。
因此我想在這裏最早說清楚它:git
也不是說這種時候一點都不能用,而是說你能夠print別的什麼東西,可是若是想看清某個變量本質的話,千萬不要用。
這個時候要用print repr(字符串)
,或者最好是在命令行或ipython裏面測試,像這樣:
github
看出區別了嗎?明確了這點,再來繼續研究編碼問題。web
unicode
和str
若是type(字符串)
顯示結果是str
,其實指的是bytes
字節碼。
而其它各類咱們所說的utf-8
,gb2312
等等也都是Unicode的不一樣實現方式。
這裏不要去考慮那麼複雜,只要先記住這兩大陣營就行。json
encoding
和decoding
絕對要記住的:
從unicode
轉換到str
,這個叫encoding
,編碼。
從str
轉換到unicode
,這個叫decoding
,解碼。
(圖片引用自知乎相關某答案。)數組
來回記住這個問題,才能進入下一步!網絡
而後來看個案例。
測試
經過上面兩種格式的對比咱們看到,str和unicode的各類區別。
那麼,既然變量裏面會出現兩種不一樣的格式,若是咱們把兩種格式的字符串連在一塊兒操做會發生什麼呢?
以下:
編碼
UnicodeDecodeError: 'ascii' codec can't decode
就這樣出現了!以上是咱們用顯性
字符串來比較兩種格式字符串的區別。
可是,咱們常常性處理python編碼問題,都不是在這種顯性
的字符串上出現的,不是從網上爬取的就是從本地文件讀取的,意思就是文件內容龐大,編碼格式很難猜到是什麼。
因此這裏咱們將問題再拆分爲兩部分討論:本地文件和網絡資源。
首先在本地創建一個有中文的以utf-8
格式保存的文本文件(實際上不管.txt仍是.md等都無所謂,內容是同樣的)。
內容只有'你好'。
上面看到,從文件讀取出來的,就是str格式的字符串。
那麼若是要把str轉化爲unicode,就要解碼,也就是decoding.
因此上面提到,必需要記住這兩個區別。
那麼若是如今我搞反了怎麼辦?就會再次出現下面錯誤:
爲了不兩種格式的字符串在一塊兒亂搞,統一他們是必須的。可是以哪種爲統一的呢,unicode仍是bytes?
網上各類文章統一口徑,要求代碼中出現全部的變量都統一爲unicode。
但是我在實踐和測試中都愈來愈發現:這種作法真的不那麼可靠,甚至我懷疑有可能咱們碰到那麼多的問題,都是由它攪亂引發的。
這樣就明白了:除了r.text返回的內容外,其它幾乎都是使用str格式,也就是bytes字節碼碼。因此咱們只要轉化requests相關的內容就行!
實際上,requests返回的response中, 除了用.text
獲取內容,咱們還能夠用.content
獲取一樣的內容,只不過是bytes格式。
那就正和咱們意,不用再去轉化每個地方的字符串,而只要盯緊這一個地方就足夠了。
先提醒下,變成unicode的過程,叫decoding
。不要記錯。
像.text
常常把ISO8859
等猜不到也檢測不到編碼(機率很低)的字符串扔過來,若是遇到的話,是很麻煩的。decoding
有兩種方法:
unicode(b'你好‘) b'你好'.decode('utf-8')
這裏由於不知道來源的編碼,因此必須用unicode()
來解碼,而不能用.decode('utf-8')
,由於顯然你不能亂寫解碼名稱,若是來源果然是(很大概率是)ISO8859
等方式,那麼錯誤的解碼確定會產生亂碼,或者直接程序報錯。切記!
因此這裏只能用unicode()
解碼。以下例:
str
格式字符串只要控制好外來源的字符串,統一爲str
,其它一切都好說!
實際上,我發現遇到的絕大多數編碼問題,實際上不是python原生方法致使的,而是這些外來庫所引發的!由於每一個模塊都會有本身的一套處理編碼的方式,你還真不知道它是採用哪一個。就像JSON的dumps()同樣埋着大坑等着咱們。因此真正應該盯緊的就是這些庫了。
下面是一個從獲取網絡資源(含中文且被requests認爲編碼是ISO8850的網頁)到本地操做且存儲到本地文件的完整測試。
import requests r = requests.get('http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue5/unipain.html') # write a webpage to local file with open('test.html', 'w') as f: f.write( r.content ) # read from a local html file with open('test.html', 'r') as f: ss = f.read()
大功告成!效果以下:
另外,關於JSON的亂碼問題,又是一個新的較長篇章。我會單分一篇,請到個人專欄裏找。