python 中的unicode是讓人很困惑袖订、比較難以理解的問題.這篇文章寫的比較好钝诚,utf-8是 unicode的一種實現(xiàn)方式昆庇,unicode、gbk衫仑、gb2312是編碼字符集.
py文件中的編碼
Python 默認(rèn)腳本文件都是ANSCII編碼的梨与,當(dāng)文件 中有非 ANSCII 編碼范圍內(nèi)的字符的時候就要使用"編碼指示"來修正一個 module 的定義中,如果.py文件中包含中文字符(嚴(yán)格的說是含有非anscii字符)文狱,則需要在第一行或第二行指定編碼聲明:# -*- coding=utf-8 -*-或者#coding=utf-8
其他的編碼如:gbk粥鞋、gb2312也可以;否則會出現(xiàn):
SyntaxError: Non-ASCII character '\xe4' in file test.py on line 3, but no encoding declared; seehttp://www.python.org/peps/pep-0263.htmlfor details
python中的編碼與解碼
先說一下python中的字符串類型瞄崇,在python中有兩種字符串類型懈糯,分別是str和unicode,他們都是basestring的派生類月褥;
str類型是一個包含Characters represent (at least) 8-bit bytes的序列欲诺;
unicode 的每個 unit 是一個 unicode obj;
在str的文檔中有這樣的一句話:
The string data type is also used to represent arrays of bytes, e.g., to hold data read from a file.
也就是說在讀取一個文件的內(nèi)容,或者從網(wǎng)絡(luò)上讀取到內(nèi)容時摹蘑,保持的對象為str類型筹燕;如果想把一個str轉(zhuǎn)換成特定編碼類型,需要把str轉(zhuǎn)為Unicode,然后從unicode轉(zhuǎn)為特定的編碼類型如:utf-8衅鹿、gb2312等撒踪。
python 編碼轉(zhuǎn)換函數(shù)
unicode 轉(zhuǎn)為 gb2312,utf-8等,使用 encode(encoding)
# -*- coding=UTF-8 -*-if__name__ =='__main__':? ? s =u'中國's_gb = s.encode('gb2312')
utf-8,GBK轉(zhuǎn)換為 unicode 使用 unicode(s,encoding) 或者 s.decode(encoding)
#coding=UTF-8if__name__ =='__main__':? ? s =u'中國'#s為unicode先轉(zhuǎn)為utf-8s_utf8 =? s.encode('UTF-8')assert(s_utf8.decode('utf-8') == s)
普通的 str 轉(zhuǎn)為 unicode,
#coding=UTF-8if__name__ =='__main__':s ='中國'su = u'中國''#s為unicode先轉(zhuǎn)為utf-8#因為s為所在的.py(# -*- coding=UTF-8 -*-)編碼為utf-8s_unicode =? s.decode('UTF-8')assert(s_unicode == su)#s轉(zhuǎn)為gb2312:先轉(zhuǎn)為unicode再轉(zhuǎn)為gb2312s.decode('utf-8').encode('gb2312')
如果直接執(zhí)行s.encode('gb2312')會發(fā)生什么?
#coding=UTF-8if__name__=='__main__':? ? s ='中國's.encode('gb2312')
這里會發(fā)生一個異常:Python 會自動的先將 s 解碼為 unicode 大渤,然后再編碼成 gb2312制妄。因為解碼是python自動進(jìn)行的,我們沒有指明解碼方式泵三,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函數(shù)
建立一個文件test.txt瞳抓,文件格式用ANSI埃疫,內(nèi)容為:"abc中文",用python來讀取
# coding=gbkprintopen("Test.txt").read()
結(jié)果:abc中文
把文件格式改成UTF-8:
結(jié)果:abc涓 枃
顯然,這里需要解碼:
# coding=gbkimportcodecsprintopen("Test.txt").read().decode("utf-8")
結(jié)果:abc中文
上面的test.txt我是用Editplus來編輯的孩哑,但當(dāng)我用Windows自帶的記事本編輯并存成UTF-8格式時栓霜,運行時報錯:
Traceback (most recent call last):File"ChineseTest.py",line3,inprintopen("Test.txt").read().decode("utf-8")UnicodeEncodeError: 'gbk' codec can'tencodecharacteru'\ufeff'inposition 0: illegal multibyte sequence
原來,某些軟件横蜒,如notepad胳蛮,在保存一個以UTF-8編碼的文件時,會在文件開始的地方插入三個不可見的字符(0xEF 0xBB 0xBF丛晌,即BOM)仅炊。因此我們在讀取時需要自己去掉這些字符,python中的codecs module定義了這個常量:
# coding=gbkimportcodecsdata=open("Test.txt").read()ifdata[:3] == codecs.BOM_UTF8:data=data[3:]printdata.decode("utf-8")
結(jié)果:abc中文
一點遺留問題
在第二部分中澎蛛,我們用unicode函數(shù)和decode方法把str轉(zhuǎn)換成unicode抚垄。為什么這兩個函數(shù)的參數(shù)用"gbk"呢?第一反應(yīng)是我們的編碼聲明里用了gbk(# coding=gbk)谋逻,但真是這樣呆馁?修改一下源文件:
# coding=utf-8s ="中文"printunicode(s,"utf-8")
運行,報錯:
Traceback (most recentcalllast):File"ChineseTest.py", line3,ins =unicode(s,"utf-8")UnicodeDecodeError:'utf8'codec can't decode bytes in position 0-1: invalid data
顯然毁兆,如果前面正常是因為兩邊都使用了gbk浙滤,那么這里我保持了兩邊utf-8一致,也應(yīng)該正常气堕,不至于報錯纺腊。
更進(jìn)一步的例子,如果我們這里轉(zhuǎn)換仍然用gbk:
# coding=utf-8s ="中文"printunicode(s,"gbk")
結(jié)果:中文
翻閱了一篇英文資料茎芭,它大致講解了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直接把字符串傳遞給操作系統(tǒng),所以你需要把str解碼成與操作系統(tǒng)一致的格式骗爆。Windows使用CP936(幾乎與gbk相同)次氨,所以這里可以使用gbk蔽介。最后測試:
# coding=utf-8s ="中文"printunicode(s,"cp936")
結(jié)果:中文
python 編碼 檢測
使用chardet可以很方便的實現(xiàn)字符串/文件的編碼檢測,例子如下:
>>>import urllib>>>rawdata = urllib.urlopen('http://www.google.cn/').read()>>>import chardet>>>chardet.detect(rawdata){'confidence':0.98999999999999999,'encoding':'GB2312'}
在工作中摘投,經(jīng)常遇到,讀取一個文件虹蓄,或者是從網(wǎng)頁獲取一個問題犀呼,明明看著是gb2312的編碼,可是當(dāng)使用decode轉(zhuǎn)時薇组,總是出錯外臂,這個時候,可以使用decode('gb18030')這個字符集來解決律胀,如果還是有問題宋光,這個時候貌矿,一定要注意,decode還有一個參數(shù)罪佳,比如逛漫,若要將某個 String對象s從gbk內(nèi)碼轉(zhuǎn)換為UTF-8,可以如下操作
s.decode('gbk').encode('utf-8′)
可是赘艳,在實際開發(fā)中酌毡,我發(fā)現(xiàn),這種辦法經(jīng)常會出現(xiàn)異常:
UnicodeDecodeError: ‘gbk' codec can't decode bytes in position 30664-30665: illegal multibyte sequence
這是因為遇到了非法字符——尤其是在某些用C/C++編寫的程序中蕾管,全角空格往往有多種不同的實現(xiàn)方式枷踏,比如\xa3\xa0,或者\xa4\x57掰曾,這些字符旭蠕,看起來都是全角空格,但它們并不是“合法”的全角空格(真正的全角空格是\xa1\xa1)旷坦,因此在轉(zhuǎn)碼的過程中出現(xiàn)了異常下梢。這樣的問題很讓人頭疼,因為只要字符串中出現(xiàn)了一個非法字符塞蹭,整個字符串——有時候孽江,就是整篇文章——就都無法轉(zhuǎn)碼。 解決辦法:
s.decode('gbk', ‘ignore').encode('utf-8′)
因為decode的函數(shù)原型是decode([encoding], [errors='strict'])番电,可以用第二個參數(shù)控制錯誤處理的策略岗屏,默認(rèn)的參數(shù)就是strict,代表遇到非法字符時拋出異常漱办;
如果設(shè)置為ignore这刷,則會忽略非法字符;
如果設(shè)置為replace娩井,則會用?取代非法字符暇屋;
如果設(shè)置為xmlcharrefreplace,則使用XML的字符引用洞辣。
參考:
http://blog.chinaunix.net/u2/68206/showart.php?id=668359
http://www.pythonclub.org/python-basic/codec
http://www.pythonclub.org/python-scripts/quanjiao-banjiao
http://www.pythonclub.org/python-basic/chardet
Edit ByMaHua