本文簡(jiǎn)單介紹了各種常用的字符編碼的特點(diǎn),并介紹了在python2.x中如何與編碼問題作戰(zhàn) :)
1. 字符編碼簡(jiǎn)介 1.2.
MBCS 這里,IBM發(fā)明了一個(gè)叫Code Page的概念,將這些編碼都收入囊中并分配頁(yè)碼,GBK是第932頁(yè),也就是CP932。所以,也可以使用CP932表示GBK。 MBCS(Multi-Byte Character Set)是這些編碼的統(tǒng)稱。目前為止大家都是用了雙字節(jié),所以有時(shí)候也叫做DBCS(Double-Byte Character Set)。必須明確的是,MBCS并不是某一種特定的編碼,Windows里根據(jù)你設(shè)定的區(qū)域不同,MBCS指代不同的編碼,而Linux里無法使用MBCS作為編碼。在Windows中你看不到MBCS這幾個(gè)字符,因?yàn)槲④洖榱烁友髿?,使用了ANSI來嚇唬人,記事本的另存為對(duì)話框里編碼ANSI就是MBCS。同時(shí),在簡(jiǎn)體中文Windows默認(rèn)的區(qū)域設(shè)定里,指代GBK。 1.3.
Unicode 最初的Unicode標(biāo)準(zhǔn)UCS-2使用兩個(gè)字節(jié)表示一個(gè)字符,所以你常??梢月牭経nicode使用兩個(gè)字節(jié)表示一個(gè)字符的說法。但過了不久有人覺得256*256太少了,還是不夠用,于是出現(xiàn)了UCS-4標(biāo)準(zhǔn),它使用4個(gè)字節(jié)表示一個(gè)字符,不過我們用的最多的仍然是UCS-2。 UCS(Unicode Character Set)還僅僅是字符對(duì)應(yīng)碼位的一張表而已,比如"漢"這個(gè)字的碼位是6C49。字符具體如何傳輸和儲(chǔ)存則是由UTF(UCS Transformation Format)來負(fù)責(zé)。 一開始這事很簡(jiǎn)單,直接使用UCS的碼位來保存,這就是UTF-16,比如,"漢"直接使用\x6C\x49保存(UTF-16-BE),或是倒過來使用\x49\x6C保存(UTF-16-LE)。但用著用著美國(guó)人覺得自己吃了大虧,以前英文字母只需要一個(gè)字節(jié)就能保存了,現(xiàn)在大鍋飯一吃變成了兩個(gè)字節(jié),空間消耗大了一倍……于是UTF-8橫空出世。 UTF-8是一種很別扭的編碼,具體表現(xiàn)在他是變長(zhǎng)的,并且兼容ASCII,ASCII字符使用1字節(jié)表示。然而這里省了的必定是從別的地方摳出來的,你肯定也聽說過UTF-8里中文字符使用3個(gè)字節(jié)來保存吧?4個(gè)字節(jié)保存的字符更是在淚奔……(具體UCS-2是怎么變成UTF-8的請(qǐng)自行搜索) 另外值得一提的是BOM(Byte Order Mark)。我們?cè)趦?chǔ)存文件時(shí),文件使用的編碼并沒有保存,打開時(shí)則需要我們記住原先保存時(shí)使用的編碼并使用這個(gè)編碼打開,這樣一來就產(chǎn)生了許多麻煩。(你可能想說記事本打開文件時(shí)并沒有讓選編碼?不妨先打開記事本再使用文件 -> 打開看看)而UTF則引入了BOM來表示自身編碼,如果一開始讀入的幾個(gè)字節(jié)是其中之一,則代表接下來要讀取的文字使用的編碼是相應(yīng)的編碼: BOM_UTF8 '\xef\xbb\xbf' 并不是所有的編輯器都會(huì)寫入BOM,但即使沒有BOM,Unicode還是可以讀取的,只是像MBCS的編碼一樣,需要另行指定具體的編碼,否則解碼將會(huì)失敗。 你可能聽說過UTF-8不需要BOM,這種說法是不對(duì)的,只是絕大多數(shù)編輯器在沒有BOM時(shí)都是以UTF-8作為默認(rèn)編碼讀取。即使是保存時(shí)默認(rèn)使用ANSI(MBCS)的記事本,在讀取文件時(shí)也是先使用UTF-8測(cè)試編碼,如果可以成功解碼,則使用UTF-8解碼。記事本這個(gè)別扭的做法造成了一個(gè)BUG:如果你新建文本文件并輸入"姹塧"然后使用ANSI(MBCS)保存,再打開就會(huì)變成"漢a",你不妨試試 :) 2. Python2.x中的編碼問題 unicode才是真正意義上的字符串,對(duì)字節(jié)串str使用正確的字符編碼進(jìn)行解碼后獲得,并且len(u'漢') == 1。 再來看看encode()和decode()兩個(gè)basestring的實(shí)例方法,理解了str和unicode的區(qū)別后,這兩個(gè)方法就不會(huì)再混淆了: 01 # coding: UTF-8 02 03 u = u'漢' 04 print repr(u) # u'\u6c49' 05 s = u.encode('UTF-8') 06 print repr(s) # '\xe6\xb1\x89' 07 u2 = s.decode('UTF-8') 08 print repr(u2) # u'\u6c49' 09 10 # 對(duì)unicode進(jìn)行解碼是錯(cuò)誤的 11 # s2 = u.decode('UTF-8') 12 # 同樣,對(duì)str進(jìn)行編碼也是錯(cuò)誤的 13 # u2 = s.encode('UTF-8') 需要注意的是,雖然對(duì)str調(diào)用encode()方法是錯(cuò)誤的,但實(shí)際上Python不會(huì)拋出異常,而是返回另外一個(gè)相同內(nèi)容但不同id的str;對(duì)unicode調(diào)用decode()方法也是這樣。很不理解為什么不把encode()和decode()分別放在unicode和str中而是都放在basestring中,但既然已經(jīng)這樣了,我們就小心避免犯錯(cuò)吧。 2.2. 字符編碼聲明 1 #-*- coding: UTF-8 -*- 實(shí)際上Python只檢查#、coding和編碼字符串,其他的字符都是為了美觀加上的。另外,Python中可用的字符編碼有很多,并且還有許多別名,還不區(qū)分大小寫,比如UTF-8可以寫成u8。參見http://docs./library/codecs.html#standard-encodings。 另外需要注意的是聲明的編碼必須與文件實(shí)際保存時(shí)用的編碼一致,否則很大幾率會(huì)出現(xiàn)代碼解析異?!,F(xiàn)在的IDE一般會(huì)自動(dòng)處理這種情況,改變聲明后同時(shí)換成聲明的編碼保存,但文本編輯器控們需要小心 :) 2.3.
讀寫文件 view source print?01 # coding: UTF-8 02 03 f = open('test.txt') 04 s = f.read() 05 f.close() 06 print type(s) # <type 'str'> 07 # 已知是GBK編碼,解碼成unicode 08 u = s.decode('GBK') 09 10 f = open('test.txt', 'w') 11 # 編碼成UTF-8編碼的str 12 s = u.encode('UTF-8') 13 f.write(s) 14 f.close() 另外,模塊codecs提供了一個(gè)open()方法,可以指定一個(gè)編碼打開文件,使用這個(gè)方法打開的文件讀取返回的將是unicode。寫入時(shí),如果參數(shù)是unicode,則使用open()時(shí)指定的編碼進(jìn)行編碼后寫入;如果是str,則先根據(jù)源代碼文件聲明的字符編碼,解碼成unicode后再進(jìn)行前述操作。相對(duì)內(nèi)置的open()來說,這個(gè)方法比較不容易在編碼上出現(xiàn)問題。 view source print?01 # coding: GBK 02 03 import codecs 04 05 f = codecs.open('test.txt', encoding='UTF-8') 06 u = f.read() 07 f.close() 08 print type(u) # <type 'unicode'> 09 10 f = codecs.open('test.txt', 'a', encoding='UTF-8') 11 # 寫入unicode 12 f.write(u) 13 14 # 寫入str,自動(dòng)進(jìn)行解碼編碼操作 15 # GBK編碼的str 16 s = '漢' 17 print repr(s) # '\xba\xba' 18 # 這里會(huì)先將GBK編碼的str解碼為unicode再編碼為UTF-8寫入 19 f.write(s) 20 f.close() 2.4. 與編碼相關(guān)的方法 view source print?01 # coding:gbk 02 03 import sys 04 import locale 05 06 def p(f): 07 print '%s.%s(): %s' % (f.__module__, f.__name__, f()) 08 09 # 返回當(dāng)前系統(tǒng)所使用的默認(rèn)字符編碼 10 p(sys.getdefaultencoding) 11 12 # 返回用于轉(zhuǎn)換Unicode文件名至系統(tǒng)文件名所使用的編碼 13 p(sys.getfilesystemencoding) 14 15 # 獲取默認(rèn)的區(qū)域設(shè)置并返回元祖(語(yǔ)言, 編碼) 16 p(locale.getdefaultlocale) 17 18 # 返回用戶設(shè)定的文本數(shù)據(jù)編碼 19 # 文檔提到this function only returns a guess 20 p(locale.getpreferredencoding) 21 22 # \xba\xba是'漢'的GBK編碼 23 # mbcs是不推薦使用的編碼,這里僅作測(cè)試表明為什么不應(yīng)該用 24 print r"'\xba\xba'.decode('mbcs'):", repr('\xba\xba'.decode('mbcs')) 25 26 #在筆者的Windows上的結(jié)果(區(qū)域設(shè)置為中文(簡(jiǎn)體, 中國(guó))) 27 #sys.getdefaultencoding(): gbk 28 #sys.getfilesystemencoding(): mbcs 29 #locale.getdefaultlocale(): ('zh_CN', 'cp936') 30 #locale.getpreferredencoding(): cp936 31 #'\xba\xba'.decode('mbcs'): u'\u6c49' 3.一些建議 3.2.
拋棄str,全部使用unicode。 3.3. 使用codecs.open()替代內(nèi)置的open()。 3.4.
絕對(duì)需要避免使用的字符編碼:MBCS/DBCS和UTF-16。 Python中編碼'MBCS'與'DBCS'是同義詞,指當(dāng)前Windows環(huán)境中MBCS指代的編碼。Linux的Python實(shí)現(xiàn)中沒有這種編碼,所以一旦移植到Linux一定會(huì)出現(xiàn)異常!另外,只要設(shè)定的Windows系統(tǒng)區(qū)域不同,MBCS指代的編碼也是不一樣的。分別設(shè)定不同的區(qū)域運(yùn)行2.4小節(jié)中的代碼的結(jié)果: 01 #中文(簡(jiǎn)體, 中國(guó)) 02 #sys.getdefaultencoding(): gbk 03 #sys.getfilesystemencoding(): mbcs 04 #locale.getdefaultlocale(): ('zh_CN', 'cp936') 05 #locale.getpreferredencoding(): cp936 06 #'\xba\xba'.decode('mbcs'): u'\u6c49' 07 08 #英語(yǔ)(美國(guó)) 09 #sys.getdefaultencoding(): UTF-8 10 #sys.getfilesystemencoding(): mbcs 11 #locale.getdefaultlocale(): ('zh_CN', 'cp1252') 12 #locale.getpreferredencoding(): cp1252 13 #'\xba\xba'.decode('mbcs'): u'\xba\xba' 14 15 #德語(yǔ)(德國(guó)) 16 #sys.getdefaultencoding(): gbk 17 #sys.getfilesystemencoding(): mbcs 18 #locale.getdefaultlocale(): ('zh_CN', 'cp1252') 19 #locale.getpreferredencoding(): cp1252 20 #'\xba\xba'.decode('mbcs'): u'\xba\xba' 21 22 #日語(yǔ)(日本) 23 #sys.getdefaultencoding(): gbk 24 #sys.getfilesystemencoding(): mbcs 25 #locale.getdefaultlocale(): ('zh_CN', 'cp932') 26 #locale.getpreferredencoding(): cp932 27 #'\xba\xba'.decode('mbcs'): u'\uff7a\uff7a' 可見,更改區(qū)域后,使用mbcs解碼得到了不正確的結(jié)果,所以,當(dāng)我們需要使用'GBK'時(shí),應(yīng)該直接寫'GBK',不要寫成'MBCS'。 UTF-16同理,雖然絕大多數(shù)操作系統(tǒng)中'UTF-16'是'UTF-16-LE'的同義詞,但直接寫'UTF-16-LE'只是多寫3個(gè)字符而已,而萬一某個(gè)操作系統(tǒng)中'UTF-16'變成了'UTF-16-BE'的同義詞,就會(huì)有錯(cuò)誤的結(jié)果。實(shí)際上,UTF-16用的相當(dāng)少,但用到的時(shí)候還是需要注意。 --END-- |
|