Python Unicode與中文處理
轉自:http://hi.baidu.com/jackleehit/blog/item/ea93618e1051131cb31bbaac.html
python中的unicode是讓人很困惑、比較難以理解的問題,本文力求徹底解決這些問題;
1.unicode、gbk、gb2312、utf-8的關係;
http://www.pythonclub.org/python-basic/encode-detail 這篇文章寫的比較好,utf-8是unicode的一種實現方式,unicode、gbk、gb2312是編碼字元集;
2.python中的中文編碼問題;
2.1 .py檔案中的編碼
Python 預設指令檔都是 ANSCII 編碼的,當檔案 中有非 ANSCII 編碼範圍內的字元的時候就要使用"編碼指示"來修正。 一個module的定義中,如果.py檔案中包含中文字元(嚴格的說是含有非anscii字元),則需要在第一行或第二行指定編碼聲明:
# -*- coding=utf-8 -*-或者 #coding=utf-8 其他的編碼如:gbk、gb2312也可以; 否則會出現類似:SyntaxError: Non-ASCII character '\xe4' in file ChineseTest.py
on line 1, but no encoding declared; see http://www.pytho for
details這樣的異常資訊;n.org/peps/pep-0263.html
2.2 python中的編碼與解碼
先說一下python中的字串類型,在python中有兩種字串類型,分別是str和unicode,他們都是basestring的派生 類;str類型是一個包含Characters represent (at least) 8-bit bytes的序列;unicode的每個unit是一個unicode obj;所以:
len(u'中國')的值是2;len('ab')的值也是2;
在str的文檔中有這樣的一句話:The string data type is also used to represent arrays of bytes, e.g., to hold data read from a file. 也就是說在讀取一個檔案的內容,或者從網路上讀取到內容時,保持的對象為str類型;如果想把一個str轉換成特定編碼類別型,需要把str轉為 Unicode,然後從unicode轉為特定的編碼類別型如:utf-8、gb2312等;
python中提供的轉換函式:
unicode轉為 gb2312,utf-8等
# -*- coding=UTF-8 -*-
if __name__ == '__main__':
s = u'中國'
s_gb = s.encode('gb2312')
utf-8,GBK轉換為unicode 使用函數unicode(s,encoding) 或者s.decode(encoding)
# -*- coding=UTF-8 -*-
if __name__ == '__main__':
s = u'中國'
#s為unicode先轉為utf-8
s_utf8 = s.encode('UTF-8')
assert(s_utf8.decode('utf-8') == s)
普通的str轉為unicode
# -*- coding=UTF-8 -*-
if __name__ == '__main__':
s = '中國'
su = u'中國''
#s為unicode先轉為utf-8
#因為s為所在的.py(# -*- coding=UTF-8 -*-)編碼為utf-8
s_unicode = s.decode('UTF-8')
assert(s_unicode == su)
#s轉為gb2312,先轉為unicode再轉為gb2312
s.decode('utf-8').encode('gb2312')
#如果直接執行s.encode('gb2312')會發生什嗎?
s.encode('gb2312')
# -*- coding=UTF-8 -*-
if __name__ == '__main__':
s = '中國'
#如果直接執行s.encode('gb2312')會發生什嗎?
s.encode('gb2312')
這裡會發生一個異常:
Python 會自動的先將 s 解碼為 unicode ,然後再編碼成 gb2312。因為解碼是python自動進行的,我們沒有指明解碼方式,python 就會使用 sys.defaultencoding 指明的方式來解碼。很多情況下 sys.defaultencoding 是 ANSCII,如果 s 不是這個類型就會出錯。
拿上面的情況來說,我的 sys.defaultencoding 是 anscii,而 s 的編碼方式和檔案的編碼方式一致,是 utf8 的,所以出錯了: UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
對於這種情況,我們有兩種方法來改正錯誤:
一是明確的指示出 s 的編碼方式
#! /usr/bin/env python
# -*- coding: utf-8 -*-
s = '中文'
s.decode('utf-8').encode('gb2312')
二是更改 sys.defaultencoding 為檔案的編碼方式
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
reload(sys) # Python2.5 初始化後會刪除 sys.setdefaultencoding 這個方法,我們需要重新載入
sys.setdefaultencoding('utf-8')
str = '中文'
str.encode('gb2312')
檔案編碼與print函數
建立一個檔案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 <module>
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中文
(四)一點遺留問題
在第二部分中,我們用unicode函數和decode方法把str轉換成unicode。為什麼這兩個函數的參數用"gbk"呢?
第一反應是我們的編碼聲明裡用了gbk(# coding=gbk),但真是這樣?
修改一下源檔案:
# coding=utf-8
s = "中文"
print unicode(s, "utf-8")
運行,報錯:
Traceback (most recent call last):
File "ChineseTest.py", line 3, in <module>
s = unicode(s, "utf-8")
UnicodeDecodeError: 'utf8' codec can't decode bytes in position 0-1: invalid data
顯然,如果前面正常是因為兩邊都使用了gbk,那麼這裡我保持了兩邊utf-8一致,也應該正常,不至於報錯。
更進一步的例子,如果我們這裡轉換仍然用gbk:
# coding=utf-8
s = "中文"
print unicode(s, "gbk")
結果:中文
翻閱了一篇英文資料,它大致講解了python中的print原理:
When Python executes a print statement, it simply passes the output to the operating system (using fwrite() or something like it), and some other program is responsible for actually displaying that output on the screen. For example, on Windows, it might be
the Windows console subsystem that displays the result. Or if you're using Windows and running Python on a Unix box somewhere else, your Windows SSH client is actually responsible for displaying the data. If you are running Python in an xterm on Unix, then
xterm and your X server handle the display.
To print data reliably, you must know the encoding that this display program expects.
簡單地說,python中的print直接把字串傳遞給作業系統,所以你需要把str解碼成與作業系統一致的格式。Windows使用CP936(幾乎與gbk相同),所以這裡可以使用gbk。
最後測試:
# coding=utf-8
s = "中文"
print unicode(s, "cp936")
結果:中文