Python:中文處理小結

文章地址:http://my.oschina.net/leejun2005/blog/74430

一、使用中文字符
在python源碼中如果使用了中文字符,運行時會有錯誤,解決的辦法是在源碼的開頭部分加入字符編碼的聲明,下面是一個例子: 
#!/usr/bin/env python 
# -*- coding: cp936 -*- 
Python Tutorial中指出,python的源文件可以編碼ASCII以外的字符集,最好的做法是在#!行後面用一個特殊的註釋行來定義字符集: 
# -*- coding: encoding -*- 
根據這個聲明,Python會嘗試將文件中的字符編碼轉爲encoding編碼,並且,它儘可能的將指定地編碼直接寫成Unicode文本。 
注意,coding:encoding只是告訴Python文件使用了encoding格式的編碼,但是編輯器可能會以自己的方式存儲.py文件,因此最後文件保存的時候還需要編碼中選指定的ecoding纔行。 
二、中文字符的存儲
>>> str = u"中文" 
>>> str 
u'\xd6\xd0\xce\xc4' 
>>> str = "中文" 
>>> str 
'\xd6\xd0\xce\xc4' 
u"中文"只是聲明unicode,實際的編碼並沒有變。這樣子就發生變化了: 
>>> str = "中文" 
>>> str 
'\xd6\xd0\xce\xc4' 
>>> str = str.decode("gb2312") 
>>> str 
u'\u4e2d\u6587' 
更進一步: 
>>> s = '中文' 
>>> s.decode('gb2312') 
u'\u4e2d\u6587' 
>>> len(s) 
4 
>>> len(s.decode('gb2312')) 
2 
>>> s = u'中文' 
>>> len(s) 
4 
>>> s = '中文test' 
>>> len(s) 
8 
>>> len(s.decode('gb2312')) 
6 
>>> s = '中文test,' 
>>> len(s) 
10 
>>> len(s.decode('gb2312')) 
7 
可以看出,對於實際Non-ASCII編碼存儲的字符串,python可以正確的識別出其中的中文字符以及中文上下文中的標點符號。 
前綴“u”表示“後面這個字符串“是一個Unicode字符串”,這僅僅是一個聲明,並不表示這個字符串就真的是Unicode了;就好比某正太聲稱自己已滿18歲,但實際上他的真實年齡並不確定,現在體育界年齡造假可不稀罕幺! 
那麼聲明成u有什麼作用呢?對於Python來說,只要你聲明某字符串是Unicode,它就會用Unicode的一套機制對它進行處理。比方說,做字符串操作的時候會動用到內部的Unicode處理函數,保存的時候以Unicode字符(雙字節)進行保存。等等。顯而易見,對於一個實際上並不是Unicode的字符串,做Unicode動作的處理,是有可能會出問題的。 u前綴只適用於你的字符串常量真的是Unicode的情況  
三、中文字符的IO操作
用python處理字符串很容易,但是在處理中文的時候需要注意一些問題。比如: 
a = "我們是python愛好者" 
print a[0] 
只能輸出“我”字的前半部分,要想輸出整個的“我”字還需要: 
b = a[0:2] 
print b 
纔行,很不方便,並且當一段文本中同時有中英文如何處理?最好的辦法就是轉換爲unicode。像這樣: 
c = unicode(a, "gb2312") 
print c[0] 
這個時候c的下標對應的就是每一個字符,不再是字節,並且通過len(c)就可以獲得字符數!還可以很方便的轉換爲其他編碼,比如轉換爲utf-8: 
d = c.encode("utf-8") 
四、<type ‘str’>和<type ‘unicode’> 
<type ‘str’>將字符串看作是字節的序列,而<type ‘unicode’>則將其看作是字符的序列,單個字符可能佔用多個字節;字節相對於字符,其在存儲層次中更低一些。 
str轉換爲unicode要decode,可以這樣想,因爲要把字節序列解釋成字符序列,字節序列是底層的存放方式,解碼(decode)成更高層的字符以便使用;同理,unicode轉換爲str要encode,就象信息編碼(encode)後才存儲一樣: 
s.decode(encoding) <type 'str'> to <type 'unicode'> 
u.encode(encoding) <type 'unicode'> to <type 'str'> 
例如: 
>>> s = 'str' 
>>> type(s) 
<type 'str'> 
>>> type(s.decode()) 
<type 'unicode'> 
>>> s = u'str' 
>>> type(s) 
<type 'unicode'> 
>>> type(s.encode()) 
<type 'str'> 
處理中文數據時最好採用如下方式: 
1. Decode early(儘早decode, 將文件中的內容轉化成unicode再進行下一步處理) 
2. Unicode everywhere (程序內部處理都用unicode) 
3. Encode late (最後encode回所需的encoding, 例如把最終結果寫進結果文件) 
下面是一個簡單的演示,用re庫查詢一箇中文字符串並打印: 
>>> p = re.compile(unicode("測試(.*)", "gb2312")) 
>>> s = unicode("測試一二三", "gb2312") 
>>> for i in p.findall(s): 
print i.encode("gb2312") 
一二三 
五、跨平臺處理技巧
如果一個project必須在兩個平臺上開發,程序應該使用同樣的encoding,比如要求所有的文件都使用UTF-8,如果實在不能統一(一般是爲了滿足許多所謂專家學者莫名其妙的要求),可以退而求其次,用當前系統編碼決定文件內的編碼: 
import locale 
import string 
import re 
#根據當前系統的encoding構造需要的編碼取值 
lang = string.upper(locale.setlocale(locale.LC_ALL, "")) 
textencoding = None 3 
#檢查編碼的值是不是滿足我們需要的情況 
if re.match("UTF-8", lang) != None: 
# UTF-8編碼 
textencoding = "utf-8" 
elif re.match(r"CHINESE|CP936", lang): 
# Windows下的GB編碼 
textencoding = "gb18030" 
elif re.match(r"GB2312|GBK|GB18030", lang): 
# Linux下的GB編碼 
textencoding = "gb18030" 
else: 
# 其他情況,拋個錯誤吧 
raise UnicodeError 
fd = file(filename, "r") 
fulltextlist = fd.readlines() 
# 把每一行轉換成unicode 
for each in len(fulltextlist): 
fulltextlist[i] = unicode(each, textencoding) 
fd.close() 
# 如果要打印的話,可以用text.encode(encoding)來恢復成多字節編碼 
六、異常處理
編碼encoding發生在Unicode字符串轉換爲字節序列時,而解碼decoding發生在字節序列轉換爲Unicode字符串時(encoding always takes a Unicode string and returns a bytes sequence, and decoding always takes a bytes sequence and returns a Unicode string)。 
UnicodeDecodeError 
UnicodeDncodeError通常發生在將str字符串解碼爲特定Unicode字符串時。由於不同的編碼只能映射部分str字符串到對應的Unicode字符,所以遇到一些字符時解碼會失敗。 
UnicodeEncodeError 
UnicodeEncodeError通常發生在將Unicode字符串編碼爲特定字節序列時。由於不同的編碼只能映射部分Unicode字符到對應的str字符串,所以遇到一些字符時編碼會失敗。 
處理python編碼轉換時的UnicodeDecodeError異常 
python提供的unicode轉換不像iconv或是mbstowcs之類的方便。如果轉換一段unicode("1234中文",'ascii') 到utf8,會直接出現UnicodeDecodeError的錯誤。如果在你能預知字串符的編碼的時候,比如你用unicode('1234中文', 'gbk') 就不會出現錯誤;不過很多時候,會出現CJK混合的情況,如果要做到將一段CJK文件轉換成unicode可能就行不通了。好在python的codecs提供了register_error這個功能: 
register_error(name, error_handler) 
原理很簡單,不過要先看unicode是如何處理異常的。unicode這個函數是將一段string按輸入的編碼轉換成目標的編碼,如果出現了不與輸入編碼相符的,會出現一個UnicodeDecodeError的異常,通常有三種處理方法:strict、replace、ignore;默認是 strict,就是直接raise UnicodeDecodeError。通過register_error,我們也可以有自己的處理方法,如果遇到與輸入的編碼不符的時候,我們就自己識別,比如GBK、BIG5、JP的字符。 
def cjk_replace(exc): 
if not isinstance(exc, UnicodeDecodeError): 
raise TypeError("don't know how to handle %r" % exc) 
if exc.end + 1 > len(exc.object): 
raise TypeError('unknown codec ,the object too short!') 
ch1 = ord(exc.object[exc.start:exc.end]) 
newpos = exc.end + 1 
ch2 = ord(exc.object[exc.start + 1:newpos]) 
sk = exc.object[exc.start:newpos] 
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0x7E<=ch2<=0xFE): # GBK 
return (unicode(sk,'cp936'), newpos) 
if 0x81<=ch1<=0xFE and (0x40<=ch2<=0x7E or 0xA1<=ch2<=0xFE): # BIG5 
return (unicode(sk,'big5'), newpos) 
raise TypeError('unknown codec !') 
codecs.register_error("cjk_replace", cjk_replace) 
我們的cjk_replace現在只能處理GBK與BIG5的,因爲我對編碼也不是特別瞭解,只是大概知道GBK與BIG5的,不太瞭解JP的。在 cjk_replace這個函數裏,我們對不認識的文字進行手工識別,如果認識的編碼,就用正確的方法,並返回編碼後的內容與新的pos,比如“1234中文”,在pos爲4的時候,會調用我們的cjk_replace,我們會返回一個從gbk轉換成utf8的“中”字,並返回下個正確的位置“文”的起始位置。當然了,處理“文”的時候,還會再調用一次。下面看看是如何使用的: 
filedata = open('test.txt','r).read() #gbk and big5 file 
data = unicode(filedata,'ascii','cjk_replace').encode('utf8') 
小結 
一個比較一般的Python中文處理的流程: 
* 將欲處理的字符串用unicode函數以正確的編碼轉換爲Unicode 
* 在程序中統一用Unicode字符串進行操作 
* 輸出時,使用encode方法,將Unicode再轉換爲所需的編碼 
有幾點要說明一下: 
* 所謂“正確的”編碼,指得是指定編碼和字符串本身的編碼必須一致。這個其實並不那麼容易判斷,一般來說,我們直接輸入的簡體中文字符,有兩種可能的編碼:GB2312(GBK、GB18030)、以及UTF-8 
* encode成本地編碼的時候,必須要保證目標編碼中存在欲轉換字符的內碼。encode這種操作一般是通過一個本地編碼對應Unicode的編碼轉換表來進行的,事實上每個本地編碼只能映射到Unicode的一部分。但是映射的區域是不同的,比如Big-5對應的Unicode的編碼範圍和 GBK對應的就不一樣(實際上這兩個編碼有部分範圍是重疊的)。所以,Unicode的一些字符(比如本身就是從GB2312轉換來的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想轉換到Big-5,很有可能就會出現編碼找不到的異常。但UTF-8的碼錶範圍實際上和Unicode是一樣的(只是編碼形式不同而已),所以,理論上來說,任何本地編碼的字符,都可以被轉換到UTF-8 
* GB2312、GBK、GB18030本質上是同一種編碼標準。只是在前者的基礎上擴充了字符數量 
* UTF-8和GB編碼不兼容 
* 出現編解碼異常時可能需要自己編寫編解碼解析函數,這需要了解一些字符編碼的知識 
參考資料 
1、  http://bbs3.chinaunix.net/thread-1389703-1-2.html
2、 Python的中文處理及其它 
http://www.go4pro.org/?p=38
3、 Python處理中文的時候的一些小技巧 
http://cocre.com/?p=461
4、 Unicode In Python, Completely Demystified. Kumar McMillan 
http://farmdev.com/talks/unicode
5、 python中文處理好方法 
http://www.okpython.com/bbs/viewthread.php?tid=311
6、 Python的中文處理 
http://hi.baidu.com/mrsz/blog/item/7812a5018c2cf2031d9583d2.html
7、 UnicodeDecodeError 
http://wiki.python.org/moin/UnicodeDecodeError
8、 UnicodeEncodeError 
http://wiki.python.org/moin/UnicodeEncodeError
9、 如何處理python編碼轉換時的UnicodeDecodeError異常 
http://blog.chinaunix.net/u/8873/showart_1009737.html
10、codecs — Codec registry and base classes 

http://docs.python.org/library/codecs.html

11、python 中文亂碼 問題深入分析

http://blog.csdn.net/kiki113/article/details/4062063

12、python新手必碰到的問題---encode與decode,中文亂碼[轉]

http://www.51testing.com/?uid-524463-action-viewspace-itemid-817888


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章