Python編碼問題詳解

繼上一篇文章字符集和編碼詳解總結(jié)了常見字符編碼后双谆,這篇文章會對python中常見的編碼問題進(jìn)行分析和總結(jié)柳洋。由于python3.x版本和python2.x版本在字符編碼方面有很大差異次伶,所以本文都是以Python2.7.5來分析2.x版本中的字符編碼問題。

1.Python編碼基礎(chǔ)

1.1 str和unicode

python中有兩種數(shù)據(jù)模型來支持字符串這種數(shù)據(jù)類型,str和unicode,它們的基類都是basestring辉懒。比如s = "中文"就是str類型的字符串,而u=u"中文"就是一個unicode類型的字符串谍失。unicode是由str類型的字符串解碼后得到眶俩,unicode也可以編碼成str類型。即

str --> decode -->unicode
unicode --> encode --> str

嚴(yán)格來說快鱼,str也許應(yīng)該叫做字節(jié)串仿便,因為對于UTF-8編碼的str類型"中文",使用len()函數(shù)得到的結(jié)果是6攒巍,因為UTF-8編碼的str類型“中文”實際是"\xe4\xb8\xad\xe6\x96\x87"。而對于unicode類型u“中文”(實際是u"\u4e2d\u6587")荒勇,使用len()函數(shù)得到結(jié)果是2.

1.2 頭部編碼聲明

在python源代碼文件中如果有用到非ascii字符柒莉,比如中文,那么需要在源碼文件頭部聲明源代碼字符編碼沽翔,格式如下:

#-*- coding: utf-8 -*-

這個格式看起比較復(fù)雜兢孝,其實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編碼解析,結(jié)果是"\xd6\xd0\xce\xc4"堤舒。

需要注意的是色建,文件本身的編碼要跟文件頭部聲明編碼一致,不然就會出現(xiàn)問題舌缤。文件本身的編碼在Linux下面可以在vim下用命令set fenc來查看箕戳。如果文件本身編碼是gbk,而源碼文件頭部聲明的編碼是utf-8国撵,這樣如果源碼中有中文就會有問題了陵吸,因為本身中文str存儲是按照gbk編碼來的,而python在解析str的時候又以為是utf-8編碼卸留,這樣就會報SyntaxError: (unicode error) 'utf8' codec can't decode byte錯誤走越。

2.2 默認(rèn)編碼問題

下面看個python默認(rèn)編碼導(dǎo)致的問題:

#coding: utf-8
u = 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最好不要直接調(diào)用decode耻瑟,str最好不要直接調(diào)用encode方法旨指。因為如果是直接調(diào)用,則相當(dāng)于u.encode(default_encoding).decode("utf-8")喳整,default_encoding是python的unicode實現(xiàn)中用的默認(rèn)編碼谆构,即sys.getdefaultencoding()得到的編碼,如果你沒有設(shè)置過,那么默認(rèn)編碼就是ascii框都,如果你的unicode本身超出了ascii編碼范圍就會報錯搬素。同理,如果對str直接調(diào)用encode方法魏保,那么默認(rèn)會先對str進(jìn)行解碼栽惶,即s.decode(default_encoding).encode("utf-8"),如果str本身是中文忆蚀,而default_encoding是ascii的話,解碼就會出錯,從而導(dǎo)致上面這兩行會分別報UnicodeEncodeError: 'ascii' codec can't encode characters in position...錯誤和UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position...錯誤墓律。

上面例子中注釋掉的兩行代碼如果執(zhí)行就會報錯猜煮,當(dāng)然舀寓,如果本身str或者unicode都在ascii編碼范圍,就沒有問題胯舷。比如s = "abc"; s.encode("utf-8")就不會有問題,語句執(zhí)行后會返回一個跟s的id不同的str绊含。

那如果要解決實例1中的問題桑嘶,有兩種方法,其一是明確指定編碼躬充,如下所示:

#coding: utf-8
u = 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的默認(rèn)編碼為文件編碼格式逃顶,如下所示(這里只所以要reload sys模塊,是因為python初始化后刪除了setdefaultencoding方法):

#coding:utf-8                                                                   

import sys 
reload(sys)
sys.setdefaultencoding("utf-8") #更改默認(rèn)編碼為utf-8

u = 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津坑,編碼就是文件本身的編碼。而調(diào)用write()寫文件時疆瑰,如果參數(shù)是unicode眉反,則需要用指定編碼encode,如果write()參數(shù)是unicode而且沒有指定編碼穆役,則會采用python默認(rèn)編碼encode后再寫入寸五。

#coding:utf-8                                                                   
f = open("testfile")
s = f.read()
f.close()
print type(s) # <type 'str'>

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參數(shù)是unicode,則使用打開文件時的編碼寫入塑悼,如果是str,則先使用默認(rèn)編碼解碼成unicode后再以打開文件的編碼寫入(這里需要注意如果str是中文厢蒜,而默認(rèn)編碼sys.getdefaultencoding()是ascii的話會報解碼錯誤)。

#coding:gbk
import codecs
 
f = codecs.open('testfile', encoding='utf-8')
u = f.read()
f.close()
print type(u) # <type 'unicode'>
 
f = codecs.open('testfile', 'a', encoding='utf-8')
f.write(u) #寫入unicode
 
# 寫入gbk編碼的str愕贡,自動進(jìn)行解碼編碼操作
s = '漢'
print repr(s) # '\xba\xba'
# 這里會先將GBK編碼的str解碼為unicode再編碼為UTF-8寫入
#f.write(s) #默認(rèn)編碼為ascii時,這會報解碼錯誤巷屿。
f.close()

3.參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市攒庵,隨后出現(xiàn)的幾起案子嘴纺,更是在濱河造成了極大的恐慌,老刑警劉巖浓冒,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栽渴,死亡現(xiàn)場離奇詭異闲擦,居然都是意外死亡墅冷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霹抛,“玉大人,你說我怎么就攤上這事端逼。” “怎么了诲祸?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵着憨,是天一觀的道長务嫡。 經(jīng)常有香客問我甲抖,道長漆改,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任准谚,我火速辦了婚禮挫剑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柱衔。我一直安慰自己樊破,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布唆铐。 她就那樣靜靜地躺著,像睡著了一般顺少。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上澳盐,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天祈纯,我揣著相機(jī)與錄音,去河邊找鬼叼耙。 笑死腕窥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的筛婉。 我是一名探鬼主播簇爆,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爽撒!你這毒婦竟也來了入蛆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤硕勿,失蹤者是張志新(化名)和其女友劉穎哨毁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體源武,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡扼褪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了粱栖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片话浇。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闹究,靈堂內(nèi)的尸體忽然破棺而出幔崖,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布赏寇,位于F島的核電站吉嫩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嗅定。R本人自食惡果不足惜率挣,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望露戒。 院中可真熱鬧,春花似錦捶箱、人聲如沸智什。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荠锭。三九已至,卻和暖如春晨川,著一層夾襖步出監(jiān)牢的瞬間证九,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工共虑, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留愧怜,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓妈拌,卻偏偏與公主長得像拥坛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尘分,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 字符集和編碼簡介 在編程中常巢峦铮可以見到各種字符集和編碼,包括ASCII,MBCS,Unicode等字符集培愁。確切的說...
    蘭山小亭閱讀 8,490評論 0 13
  • 幾個基本概念 bit二進(jìn)制位著摔, 是計算機(jī)內(nèi)部數(shù)據(jù)儲存的最小單位,11010100是一個8位二進(jìn)制數(shù)定续。一個二進(jìn)制位只...
    西電大俠閱讀 3,571評論 1 8
  • 可以看我的博客 lmwen.top 或者訂閱我的公眾號 簡介有稍微接觸python的人就會知道谍咆,python中...
    ayuLiao閱讀 3,115評論 1 5
  • 個人筆記,方便自己查閱使用 Py.LangSpec.Contents Refs Built-in Closure ...
    freenik閱讀 67,704評論 0 5
  • 1 字符編碼簡介 1.1 ASCII ASCII:American Standard Code for Infor...
    hufengreborn閱讀 8,336評論 3 23