- Python的中文處理
- 一、使用中文字符
- 在python源碼中如果使用了中文字符,運行時會有錯誤,解決的辦法是在源碼的開頭部分加入字符編碼的聲明,下面是一個例子:
- #!/usr/bin/env python
- # -*- coding: cp936 -*-
- Python Tutorial中指出,python的源文件可以編碼ASCII以外的字符集,最好的做法是在#!行后面用一個特殊的注釋行來定義字符集:
- # -*- coding: encoding -*-
- 根據(jù)這個聲明,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,實際的編碼并沒有變。這樣子就發(fā)生變化了:
- >>> 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歲,但實際上他的真實年齡并不確定,現(xiàn)在體育界年齡造假可不稀罕幺!
- 那么聲明成u有什么作用呢?對于Python來說,只要你聲明某字符串是Unicode,它就會用Unicode的一套機制對它進行處理。比方說,做字符串操作的時候會動用到內(nèi)部的Unicode處理函數(shù),保存的時候以Unicode字符(雙字節(jié))進行保存。等等。顯而易見,對于一個實際上并不是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的下標對應的就是每一個字符,不再是字節(jié),并且通過len(c)就可以獲得字符數(shù)!還可以很方便的轉換為其他編碼,比如轉換為utf-8:
- d = c.encode("utf-8")
- 四、<type 'str’>和<type 'unicode’>
- <type 'str’>將字符串看作是字節(jié)的序列,而<type 'unicode’>則將其看作是字符的序列,單個字符可能占用多個字節(jié);字節(jié)相對于字符,其在存儲層次中更低一些。
- str轉換為unicode要decode,可以這樣想,因為要把字節(jié)序列解釋成字符序列,字節(jié)序列是底層的存放方式,解碼(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'>
- 處理中文數(shù)據(jù)時最好采用如下方式:
- 1. Decode early(盡早decode, 將文件中的內(nèi)容轉化成unicode再進行下一步處理)
- 2. Unicode everywhere (程序內(nèi)部處理都用unicode)
- 3. Encode late (最后encode回所需的encoding, 例如把最終結果寫進結果文件)
- 下面是一個簡單的演示,用re庫查詢一個中文字符串并打?。?br>
- >>> p = re.compile(unicode("測試(.*)", "gb2312"))
- >>> s = unicode("測試一二三", "gb2312")
- >>> for i in p.findall(s):
- print i.encode("gb2312")
- 一二三
- 五、跨平臺處理技巧
- 如果一個project必須在兩個平臺上開發(fā),程序應該使用同樣的encoding,比如要求所有的文件都使用UTF-8,如果實在不能統(tǒng)一(一般是為了滿足許多所謂專家學者莫名其妙的要求),可以退而求其次,用當前系統(tǒng)編碼決定文件內(nèi)的編碼:
- import locale
- import string
- import re
- #根據(jù)當前系統(tǒng)的encoding構造需要的編碼取值
- lang = string.upper(locale.setlocale(locale.LC_ALL, ""))
- textencoding = None
- #檢查編碼的值是不是滿足我們需要的情況
- 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)來恢復成多字節(jié)編碼
- 小結
- 一個比較一般的Python中文處理的流程:
- * 將欲處理的字符串用unicode函數(shù)以正確的編碼轉換為Unicode
- * 在程序中統(tǒng)一用Unicode字符串進行操作
- * 輸出時,使用encode方法,將Unicode再轉換為所需的編碼
- 有幾點要說明一下:
- * 所謂“正確的”編碼,指得是指定編碼和字符串本身的編碼必須一致。這個其實并不那么容易判斷,一般來說,我們直接輸入的簡體中文字符,有兩種可能的編碼:GB2312(GBK、GB18030)、以及UTF-8
- * encode成本地編碼的時候,必須要保證目標編碼中存在欲轉換字符的內(nèi)碼。encode這種操作一般是通過一個本地編碼對應Unicode的編碼轉換表來進行的,事實上每個本地編碼只能映射到Unicode的一部分。但是映射的區(qū)域是不同的,比如Big-5對應的Unicode的編碼范圍和 GBK對應的就不一樣(實際上這兩個編碼有部分范圍是重疊的)。所以,Unicode的一些字符(比如本身就是從GB2312轉換來的那些),可以映射到 GBK,但未必可以映射到Big-5,如果你想轉換到Big-5,很有可能就會出現(xiàn)編碼找不到的異常。但UTF-8的碼表范圍實際上和Unicode是一樣的(只是編碼形式不同而已),所以,理論上來說,任何本地編碼的字符,都可以被轉換到UTF-8
- * GB2312、GBK、GB18030本質上是同一種編碼標準。只是在前者的基礎上擴充了字符數(shù)量
- * UTF-8和GB編碼不兼容
- 參考資料
- 1、[url]http://bbs3./thread-1389703-1-2.html[/url]
- 2、Python的中文處理及其它
- [url]http://www./?p=38[/url]
- 3、Python處理中文的時候的一些小技巧
- [url]http:///?p=461[/url]
- 4、Unicode In Python, Completely Demystified. Kumar McMillan
- [url]http:///talks/unicode[/url]
- 5、python中文處理好方法
- [url]http://www./bbs/viewthread.php?tid=311[/url]
- 6、Python的中文處理
- [url]http://hi.baidu.com/mrsz/blog/item/7812a5018c2cf2031d9583d2.html[/url]
復制代碼 [ 本帖最后由 pFreeStyle 于 2009-4-16 09:24 編輯 ] |
-
-
Python的中文處理.pdf
108.54 KB, 下載次數(shù): 581
PDF版
-
-
Python的中文處理.pdf
124.4 KB, 下載次數(shù): 683
添加了異常處理的介紹
-
- 六、異常處理
- 編碼encoding發(fā)生在Unicode字符串轉換為字節(jié)序列時,而解碼decoding發(fā)生在字節(jié)序列轉換為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通常發(fā)生在將str字符串解碼為特定Unicode字符串時。由于不同的編碼只能映射部分str字符串到對應的Unicode字符,所以遇到一些字符時解碼會失敗。
- UnicodeEncodeError
- UnicodeEncodeError通常發(fā)生在將Unicode字符串編碼為特定字節(jié)序列時。由于不同的編碼只能映射部分Unicode字符到對應的str字符串,所以遇到一些字符時編碼會失敗。
- 處理python編碼轉換時的UnicodeDecodeError異常
- python提供的unicode轉換不像iconv或是mbstowcs之類的方便。如果轉換一段unicode("1234中文",'ascii') 到utf8,會直接出現(xiàn)UnicodeDecodeError的錯誤。如果在你能預知字串符的編碼的時候,比如你用unicode('1234中文', 'gbk') 就不會出現(xiàn)錯誤;不過很多時候,會出現(xiàn)CJK混合的情況,如果要做到將一段CJK文件轉換成unicode可能就行不通了。好在python的codecs提供了register_error這個功能:
- register_error(name, error_handler)
- 原理很簡單,不過要先看unicode是如何處理異常的。unicode這個函數(shù)是將一段string按輸入的編碼轉換成目標的編碼,如果出現(xiàn)了不與輸入編碼相符的,會出現(xiàn)一個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現(xiàn)在只能處理GBK與BIG5的,因為我對編碼也不是特別了解,只是大概知道GBK與BIG5的,不太了解JP的。在 cjk_replace這個函數(shù)里,我們對不認識的文字進行手工識別,如果認識的編碼,就用正確的方法,并返回編碼后的內(nèi)容與新的pos,比如“1234中文”,在pos為4的時候,會調(diào)用我們的cjk_replace,我們會返回一個從gbk轉換成utf8的“中”字,并返回下個正確的位置“文”的起始位置。當然了,處理“文”的時候,還會再調(diào)用一次。下面看看是如何使用的:
- filedata = open('test.txt','r).read() #gbk and big5 file
- data = unicode(filedata,'ascii','cjk_replace').encode('utf8')
- 7、 UnicodeDecodeError
- [url]http://wiki./moin/UnicodeDecodeError[/url]
- 8、 UnicodeEncodeError
- [url]http://wiki./moin/UnicodeEncodeError[/url]
- 9、 如何處理python編碼轉換時的UnicodeDecodeError異常
- [url]http://blog./u/8873/showart_1009737.html[/url]
- 10、codecs — Codec registry and base classes
- [url]http://docs./library/codecs.html[/url]
|