對於很多接觸Python的人而言,字元的處理和語言整體的溫順可靠相比顯得格外桀驁不馴難以駕馭。
本文不談複雜的理論,就經驗教你字元處理八字真言:確定編碼,同類互動。
文章針對Python 2.7,主要因為3對的編碼已經有了很大的改善並且實際原理一樣,更改一下操作命令即可。
瞭解完本文,你可以輕鬆解決文書處理,特殊平臺(Windows?)下的編碼,爬蟲編碼等問題。
閱讀建議
本文分為如下幾個部分:
- 原理
- 具體操作
- 建議的使用習慣
- 疑難問題解答
如果想要了解我給出的使用習慣,可以直接跳到建議的使用習慣。
如果只想要解決相關問題可以直接跳到疑難問題解答。
希望本文能夠幫到你。
原理
為了理解方便,這裡不談理論只做類比,具體想要進一步瞭解各種編碼的理論的搜狗一下好了。
首先說一下我們為什麼會碰到各式各樣的編碼問題:
- 因為我們沒有統一編碼
- 因為我們沒有用對命令(傳對資料)
再說一下編碼是什麼,Python的編碼看似複雜,實際上可以看做只有兩類編碼:Unicode,二進位制
- Unicode 相信都很熟悉:,就是
\u0000
這樣的 - 二進位制編碼也很簡單,就是
\x00\x00
這樣的,平常看到的utf-8
,cp936
都是二進位制編碼 - 二進位制編碼是具象的,
10001100
原樣就可以儲存,而Unicode是抽象的,不能這樣存
1 2 3 4 5 6 7 8 9 10 11 |
#coding=utf8 # Unicode編碼演示 print('Unicode:') print(repr('Unicode編碼'))` # 二進位制編碼演示 print(u'二進位制編碼:') print(repr('Unicode編碼'))` # 只是看個樣子,程式碼不必去深究 |
再說怎麼做,就是隻有同種編碼之間才可以操作
- 舉個簡單的類比
1234就把一串資料比為烤鴨,我們作為人和鴨子不同種看待烤鴨的態度完全不一樣。我們看到的是晚上的配菜,鴨子看到的是自己二舅。那麼我在逛烤鴨店的時候用錯編碼就會報錯。因為我在烤鴨店看到了滿世界的二舅。 - 這裡說的同種就是我們熟悉的各種編碼方式:
utf-8
,unicode
,ucs-bom
- 這也就是編碼問題的核心,非常重要。
最後說一下Python的環境
- 本身程式碼是用Ascii解碼的,檔案裡有Ascii無法解碼的內容的話要告知Python怎麼解碼
- 內部大量命令都是預設接受Unicode
1 2 3 |
# 告知的命令就是下面這一行,刪掉就會報錯 #coding=utf8 print(u'測試編碼') |
具體操作
拿到各種編碼的內容自然是不用說,那麼如果我們想要自己構造怎麼做呢,看下面:
1 2 3 4 5 6 7 8 9 |
#coding=utf8 # 字串前面加u會預設構造出Unicode的字串 unicodeString = u'Unicode字串' # 字串前面什麼都不加會構造出預設編碼(首行限定了現在的utf8)的字串 utf8String = 'Utf-8字串' # 當然,沒有首行,預設的編碼是Ascii |
那麼他們之間怎麼轉換呢,同樣很簡單:
1 2 3 4 5 6 7 8 9 10 |
# 接上一段程式 # Unicode轉化為二進位制編碼中的一種:utf8 unicodeString.encode('utf8') # 二進位制編碼根據自己的編碼種類轉化為Unicode utf8String.decode('utf8') # 如果二進位制編碼中混進了奇怪的東西可以根據需求用特殊的decode策略 print(repr('u8字\x00符串'.decode('utf8', 'replace'))) |
那麼怎麼樣會出現問題呢:
1 2 3 4 5 6 7 8 9 10 11 |
# 接上一段程式 # 如果我們把他們轉化成同樣的編碼方式就可以操作(例如相加) print(repr(unicodeString + utf8String.decode('utf8'))) print(repr(unicodeString.encode('utf8') + utf8String)) # 但如果不轉化,當然就會出現滿世界的烤鴨二舅啦 unicodeString + utf8String # 所以另一方面也發現,編碼轉換是需要我們告訴程式怎麼做的 # 所有`decode`操作都會生成Unicode編碼,這是為了方便我之前說的大量接受Unicode的內部命令 |
所以我們需要確定程式使用的編碼,這是我們需要告訴程式的東西
- 一方面在操作字串的時候確定是同種編碼
- 另一方面在使用非自己寫的命令時,一般使用Unicode,或者使用接收二進位制編碼的命令
1 2 3 4 5 6 7 8 9 10 11 |
#coding=utf8 # 這裡拿寫入檔案舉例 # 一般使用Unicode with open('Unicode.txt', 'w') as f: f.write(u'Unicode測試') # 或者使用接收二進位制編碼的命令 with open('Utf8.txt', 'wb') as f: f.write('Utf8測試') # 你可以反過來做個測試,自然會報錯 # 二進位制的命令方便了在不知道怎麼解碼的情況下也能進行操作(寫入檔案) |
我建議的使用習慣
相信到這裡我已經把我對於編碼的理解講完了。
我們為什麼會碰到各式各樣的編碼問題:
- 因為我們沒有統一編碼
- 因為我們沒有用對命令(傳對資料)
所以這裡再重申一下八字真言:確定編碼,同類互動
- 碰到問題,問一下自己,我現在是哪種編碼
- 同一種編碼才能互動,那我應該是哪種編碼
這裡給出我的使用習慣:
- 確定一種內部編碼
- 內部編碼的選擇優先順序如下:程式必須使用的編碼、第三方包使用的編碼、你喜歡的編碼、Unicode
- 在輸出時再更改到特定的編碼
記得在開始整個程式之前確定內部的編碼,否則編碼一團糟會產生很多不必要的bug。
不要迷信內部Unicode,例如Evernote開發就應該根據第三方包使用的Utf8確定內部編碼。
疑難問題解答
編碼識別
說了要確定編碼,那麼拿到一串二進位制要怎麼確定編碼呢?
最簡單的方法是chardet
:(需要安裝)
1 |
python -m pip install chardet |
使用非常簡單:
1 2 3 4 5 6 |
#coding=utf8 from chardet import detect print(detect('這是一串utf8的測試字元')) # 結果:`{'confidence': 0.99, 'encoding': 'utf-8'}` |
另外例如抓取網站,那麼標頭檔案中很有可能有提示如何解碼,記得不要忘記了。
編碼轉換
很可能因為字串中參雜了奇怪的東西,導致即使編碼種類正確,依舊無法解碼。
我知道我之前講過了,但可能有人直接跳疑難問題解答嘛。
這裡可以使用decode
的第二個引數:
1 2 3 4 5 6 7 8 |
#coding=utf8 # 字串中混進了\x00 rubbishUtf8String = 'Utf-8字\x00符串' print(repr(rubbishUtf8String.decode('utf8', 'replace'))) print(repr(rubbishUtf8String.decode('utf8', 'ignore'))) |
特殊平臺下編碼
很多人都說Windows是個坑,即使在Python 3下面也一樣。
因為中文檔名出來都是亂碼。
這裡使用一個取巧的方法:平臺編碼再特殊,起碼命令列讀取和建立一個資料夾不會出亂碼吧。
1 2 3 4 |
import sys, os for folder in os.walk('.').next()[1]: print(folder.decode(sys.stdin.encoding)) |
同樣的輸入輸出也可以這樣做優化:
1 2 3 4 5 6 7 |
import sys def sys_print(msg): print(msg.encode(sys.stdin.encoding)) def sys_input(msg): return raw_input(msg.encode(sys.stdin.encoding)).decode(sys.stdin.encoding) |
檔案寫入
如果抓下來一個內容不知道怎麼解碼,但還是想要寫入檔案怎麼辦
寫入檔案的時候制定用二進位制命令即可:
1 2 3 4 5 6 7 8 9 |
#coding=utf8 import urllib with open('Utf8.txt', 'wb') as f: f.write('Utf8測試') # 比如抓了個網頁,不知道編碼也可以寫入檔案進行一系列操作 content = urllib.urlopen('http://www.baidu.com').read() with open('baidu.txt', 'wb') as f: f.write(content) |
裸Unicode字元
Unicode存成六個Ascii字元怎麼辦?其實也可以decode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#coding=utf8 # 這是普通的Unicode s = u'測' for i in s: print(i) print(repr(s)) # 這是裸Unicode,實際存成了六個Ascii s = repr(s)[2:-1] for i in s: print(i) print(repr(s)) # 轉化其實也很簡單 s = s.decode('unicode-escape') for i in s: print(i) print(repr(s)) |
結束語
希望讀完這篇文章能對你有幫助,有什麼不足之處萬望指正(鞠躬)。
有什麼想法或者想要關注我的更新,歡迎來Github上Star或者Fork我的專案。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!