標籤:
1.Python編碼基礎1.1 str和unicode
python中有兩種資料模型來支援字串這種資料類型,str和unicode,它們的基類都是basestring。比如s = "中文"
就是str類型的字串,而u=u"中文"
就是一個unicode類型的字串。unicode是由str類型的字串解碼後得到,unicode也可以編碼成str類型。即
str --> decode -->unicodeunicode --> encode --> str
嚴格來說,str也許應該叫做位元組串,因為對於UTF-8編碼的str類型"中文",使用len()函數得到的結果是6,因為UTF-8編碼的str類型“中文”
實際是"\xe4\xb8\xad\xe6\x96\x87"
。而對於unicode類型u“中文”(實際是u"\u4e2d\u6587"
),使用len()函數得到結果是2.
1.2 頭部編碼聲明
在python原始碼檔案中如果有用到非ascii字元,比如中文,那麼需要在源碼檔案頭部聲明原始碼字元編碼,格式如下:
#-*- coding: utf-8 -*-
這個格式看起比較複雜,其實python只檢查#、coding,編碼等字串,可以簡寫成#coding:utf-8,甚至還可以寫成#coding:u8。
2.Python2.x常見編碼問題2.1 頭部編碼聲明和檔案編碼問題
檔案頭部編碼聲明決定了python解析源碼中的str的編碼選擇方式,比如頭部聲明的是utf-8編碼,則代碼中s="中文"
python就會按照utf-8編碼格式來解析,通過repr(s)
可以看到字元編碼是"\xe4\xb8\xad\xe6\x96\x87"
,如果頭部聲明的編碼是gbk編碼,則python會對s採用gbk編碼解析,結果是"\xd6\xd0\xce\xc4"
。
需要注意的是,檔案本身的編碼要跟檔案頭部聲明編碼一致,不然就會出現問題。檔案本身的編碼在Linux下面可以在vim下用命令set fenc
來查看。如果檔案本身編碼是gbk,而源碼檔案頭部聲明的編碼是utf-8,這樣如果源碼中有中文就會有問題了,因為本身中文str儲存是按照gbk編碼來的,而python在解析str的時候又以為是utf-8編碼,這樣就會報SyntaxError: (unicode error) ‘utf8‘ codec can‘t decode byte
錯誤。
2.2 預設編碼問題
下面看個python預設編碼導致的問題:
#coding: utf-8u = u"中文"print repr(u) # u‘\u4e2d\u6587‘s = "中文"print repr(s) # ‘\xe4\xb8\xad\xe6\x96\x87‘u2 = s.decode("utf-8")print repr(u2) # u‘\u4e2d\u6587‘#s2 = u.decode("utf-8") #編碼錯誤#u2 = s.encode("utf-8") #解碼錯誤
注意執行個體中注釋掉的2行代碼,對於unicode最好不要直接調用decode,str最好不要直接調用encode方法。因為如果是直接調用,則相當於u.encode(default_encoding).decode("utf-8")
,default_encoding是python的unicode實現中用的預設編碼,即sys.getdefaultencoding()
得到的編碼,如果你沒有設定過,那麼預設編碼就是ascii,如果你的unicode本身超出了ascii編碼範圍就會報錯。同理,如果對str直接調用encode方法,那麼預設會先對str進行解碼,即s.decode(default_encoding).encode("utf-8"),如果str本身是中文,而default_encoding是ascii的話,解碼就會出錯,從而導致上面這兩行會分別報UnicodeEncodeError: ‘ascii‘ codec can‘t encode characters in position...
錯誤和UnicodeDecodeError: ‘ascii‘ codec can‘t decode byte 0xe4 in position...
錯誤。
上面例子中注釋掉的兩行代碼如果執行就會報錯,當然,如果本身str或者unicode都在ascii編碼範圍,就沒有問題。比如s = "abc"; s.encode("utf-8")
就不會有問題,語句執行後會返回一個跟s的id不同的str。
那如果要解決執行個體1中的問題,有兩種方法,其一是明確指定編碼,如下所示:
#coding: utf-8u = u"中文"print repr(u) # u‘\u4e2d\u6587‘s = "中文"print repr(s) # ‘\xe4\xb8\xad\xe6\x96\x87‘u2 = s.decode("utf-8")print repr(u2) # u‘\u4e2d\u6587‘s2 = u.encode("utf-8").decode("utf-8") # OK u2 = s.decode("utf8").encode("utf-8") # OK
第二種方法就是更改python的預設編碼為檔案編碼格式,如下所示(這裡只所以要reload sys模組,是因為python初始化後刪除了setdefaultencoding方法):
#coding:utf-8 import sys reload(sys)sys.setdefaultencoding("utf-8") #更改預設編碼為utf-8u = u"中文"print repr(u) # u‘\u4e2d\u6587‘s = "中文"print repr(s) # ‘\xe4\xb8\xad\xe6\x96\x87‘u2 = s.decode("utf-8")print repr(u2) # u‘\u4e2d\u6587‘s2 = u.decode("utf-8")u2 = s.encode("utf-8")
2.3讀寫檔案編碼
採用python的open()方法開啟檔案時,read()讀取的是str,編碼就是檔案本身的編碼。而調用write()寫檔案時,如果參數是unicode,則需要用指定編碼encode,如果write()參數是unicode而且沒有指定編碼,則會採用python預設編碼encode後再寫入。
#coding:utf-8 f = open("testfile")s = f.read()f.close()print type(s) # u = s.decode("utf-8") #testfile是utf-8編碼f = open("testfile", "w")f.write(u.encode("gbk")) #以gbk編碼寫入,testfile為gbk編碼f.close()
此外,python的codecs模組提供了一個open()方法,可以指定編碼開啟檔案,使用這個方法開啟檔案讀取返回是unicode。寫入時,如果write參數是unicode,則使用開啟檔案時的編碼寫入,如果是str,則先使用預設編碼解碼成unicode後再以開啟檔案的編碼寫入(這裡需要注意如果str是中文,而預設編碼sys.getdefaultencoding()是ascii的話會報解碼錯誤)。
#coding:gbkimport codecsf = codecs.open(‘testfile‘, encoding=‘utf-8‘)u = f.read()f.close()print type(u) # f = codecs.open(‘testfile‘, ‘a‘, encoding=‘utf-8‘)f.write(u) #寫入unicode# 寫入gbk編碼的str,自動進行解碼編碼操作s = ‘漢‘print repr(s) # ‘\xba\xba‘# 這裡會先將GBK編碼的str解碼為unicode再編碼為UTF-8寫入#f.write(s) #預設編碼為ascii時,這會報解碼錯誤。f.close()
參考
http://www.2cto.com/kf/201407/317866.html
Python編碼問題詳解