python 字元編碼處理總結
問題引入
python中經常遇到這樣那樣的字元編碼問題,尤其在處理網頁原始碼時(特別是爬蟲中):
UnicodeDecodeError: ‘XXX' codec can't decode bytes in position 12-15: illegal multibyte...
每次看到上面這段文字的時候,感覺整個世界都昏暗了,然後就只能各種搜尋找資料,過後就忘了。下次遇到時就讓世界再昏暗一次。為了徹底解決這個攔路虎,今天咱們就好好的來嘮嗑嘮嗑。
下面以漢字'哈'來解釋作示例解釋所有的問題,漢字“哈”的各種編碼如下:
1 UNICODE(UTF8-16): 0xC854
2 UTF-8: 0xE59388
3 GBK: 0xB9FE
除此之外還有如gb2312, big5等。例如一些含有繁體字的頁面,比如www.google.com.hk首頁中用的就是big5碼,
不知道港臺的碼農同時處理簡體字繁體字是不是更鬱悶(笑臉)
處理解決
首先,在python中提到unicode,一般指的是unicode物件,例如'哈哈'的unicode物件為u'\u54c8\u54c8'
而str是一個位元組陣列,這個位元組陣列表示的是對unicode物件編碼後(如utf-8、gbk、cp936、GB2312)的儲存的格式,這裡它僅是一個位元組流,沒有其它的含義,如果你想使這個位元組流顯示的內容有意義,就必須用正確的編碼格式,解碼顯示。
例如:(注意是在windows下)
s = u'哈哈'
s_utf8 = s.encode('utf-8')
pirnt s_utf8
>>> 鍝堝搱
悲劇...
s_utf8這時實際上是'\xe5\x93\x88\xe5\x93\x88'
而下面的程式碼才可以正常顯示:
s_gdb = s.encode('gbk') # s_gdk 這時是'\xb9\xfe\xb9\xfe'
print s_gbk
>>> 哈哈 #正常了
因為print語句它的實現是將要輸出的內容傳 送了作業系統,作業系統會根據系統的編碼對輸入的位元組流進行編碼,這就解釋了utf-8格式的字串“哈哈”,輸出的是“鍝堝搱”,因為 '\xe5\x93\x88\xe5\x93\x88'用GB2312去解釋,其顯示的出來就“鍝堝搱”。
這裡再強調一下,str記錄的是位元組陣列,只是某種編碼的儲存格式,至於輸出到檔案或是打印出來是什麼格式,完全取決其解碼的編碼將它解碼成什麼樣子。
str和unicode物件的轉換,通過encode和decode實現,具體使用如下:再次強調windows下:
s = '哈哈'
print s.decode('gbk').encode('utf-8')
>>> 鍝堝搱
反之亦然,有興趣可以嘗試其他轉換
有時當我們遇到把s(gbk字串)直接編碼成utf-8的時候,將丟擲異常,但是通過呼叫如下程式碼:
import sys
reload(sys)
sys.setdefaultencoding('gbk')
後就可以轉換成功,為什麼呢?
在python中str和unicode在編碼和解碼過程中,如果將一個str直接編碼成另一種編碼,會先把str解碼成unicode,採用預設編碼,一般預設編碼是anscii,所以在上面示例程式碼中第一次轉換的時候會出錯,
當設定當前預設編碼為'gbk'後,就不會出錯了。至於reload(sys)是因為Python2.5 初始化後會刪除 sys.setdefaultencoding 這個方法,我們需要重新載入。一般不推薦這樣使用。本來reload都是應該避免使用的函式。
對於操作不同檔案的編碼格式的檔案,也會遇到這樣的問題
建立一個檔案test.txt,檔案格式用ANSI,內容為:
abc中文
然後用python來讀取
# coding=gbk
print open("Test.txt").read()
結果:abc中文
把檔案格式改成UTF-8:
結果:abc涓枃,顯然,這裡需要解碼:
# coding=gbk
import codecs
print open("Test.txt").read().decode("utf-8")
結果:abc中文
上面的test.txt我是用Editplus來編輯的,但當我用Windows自帶的記事本編輯並存成UTF-8格式時,
執行時報錯:
Traceback (most recent call last):
File "ChineseTest.py", line 3, in
print open("Test.txt").read().decode("utf-8")
UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence
原來,某些軟體,如notepad,在儲存一個以UTF-8編碼的檔案時,
會在檔案開始的地方插入三個不可見的字元(0xEF 0xBB 0xBF,即BOM)。
因此我們在讀取時需要自己去掉這些字元,python中的codecs module定義了這個常量:
# coding=gbk
import codecs
data = open("Test.txt").read()
if data[:3] == codecs.BOM_UTF8:
data = data[3:]
print data.decode("utf-8")
結果:abc中文
最後,有些時候編碼搞對了,但是遇到了非法字元,比如產生字串的來源發生錯誤,引入了錯誤值等,這時再次遇到異常
例如:全形空格往往有多種不同的實現方式,比如\xa3\xa0,或者\xa4\x57,
這些字元,看起來都是全形空格,但它們並不是“合法”的全形空格
真正的全形空格是\xa1\xa1,因此在轉碼的過程中出現了異常。
而之前在處理新浪微博資料時,遇到了非法空格問題導致無法正確解析資料。
解決辦法:
將獲取的字串strTxt做decode時,指明ignore,會忽略非法字元,
當然對於gbk等編碼,處理同樣問題的方法是類似的
strTest = strTxt.decode('utf-8', 'ignore')
return strTest
預設的引數就是strict,代表遇到非法字元時丟擲異常;
如果設定為ignore,則會忽略非法字元;
如果設定為replace,則會用?號取代非法字元;
如果設定為xmlcharrefreplace,則使用XML的字元引用。