文章地址: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