1.基本概念
字符集(Characterset)
解釋?zhuān)何淖趾头系目偡Q(chēng)
常見(jiàn)字符集:
Unicode字符集
ASCII字符集(Unicode子集)
GB2312字符集
編碼方法(Encoding)
解釋?zhuān)簩⒆址麑?duì)應(yīng)到字節(jié)的方法,部分字符集和編碼方法名稱(chēng)一樣。
常見(jiàn)編碼方法:
UTF-8:可對(duì)Unicode字符進(jìn)行編碼
GB2312
ASCII
編碼(Encode)
解釋?zhuān)簩⒆址凶址凑找欢ㄒ?guī)則轉(zhuǎn)換成字節(jié)
解碼(Decode)
解釋?zhuān)号c編碼相反,將字節(jié)轉(zhuǎn)換為字符集中的字符
字符集與編碼方法的關(guān)系
每個(gè)字符集都有對(duì)應(yīng)的編碼方法
一種字符集可能有多種編碼方法
不同的編碼方法得到的字節(jié)不同,占用存儲(chǔ)空間也不一樣
例如Unicode字符可以使用UTF-8/ASCII/GBK等方法編碼
Unicode字符集包含世界上大部分字符,很多其他字符集有的字符它都有,是他們的超集
大部分字符集可以理解為Unicode的子集
實(shí)際上,除了Unicode之外所謂的字符集主要是對(duì)Unicode部分字符編碼而已(定義編碼方式)
一種編碼不必支持Unicode的所有字符(通常把它能支持的那部分稱(chēng)為它的字符集)
2.關(guān)于編碼的錯(cuò)誤和解決方法
在開(kāi)發(fā)過(guò)程中,我們所接觸的字符集大多都是Unicode,大部分報(bào)錯(cuò)都是關(guān)于編碼和解碼的。
2.1.編碼錯(cuò)誤UnicodeEncodeError
2.1.1.錯(cuò)誤分析
導(dǎo)致該錯(cuò)誤的原因通常是編碼方法支持的Unicode字符不全;在工作中,你寫(xiě)了一個(gè)txt中文文檔,想用ascii編碼去保存這個(gè)文件,就會(huì)報(bào)這種錯(cuò)誤。
錯(cuò)誤復(fù)現(xiàn):
我們知道ascii不支持字符中,那我們用ascii編碼方法對(duì)Unicode碼中進(jìn)行編碼:
#-*-encoding:utf-8-*-
u"中".encode(encoding='ascii')
報(bào)錯(cuò)如下:
UnicodeEncodeError:'ascii'codeccan'tencodecharacter'\u4e2d'inposition0:ordinalnotinrange(128)
這是一個(gè)UnicodeEncodeError類(lèi)型的錯(cuò)誤,提示無(wú)法使用指定的編碼方法對(duì)字符進(jìn)行編碼,報(bào)錯(cuò)提示中可以得到3個(gè)信息:
當(dāng)前使用的是acsii編碼方法
被編碼的字符是'\u4e2d'
ascii編碼方法能支持的字符有128個(gè)
有時(shí)候我們還可以利用這個(gè)提示查看編碼方法支持的字符個(gè)數(shù):
#-*-encoding:utf-8-*-
u"中".encode(encoding='iso-8859-1')
報(bào)錯(cuò):
UnicodeEncodeError:'latin-1'codeccan'tencodecharacter'\u4e2d'inposition0:ordinalnotinrange(256)
通過(guò)報(bào)錯(cuò)提示可以看出iso-8859-1能編256個(gè)字符。
接著,我們來(lái)看下用支持中文的utf-8編碼方法進(jìn)行編碼能得到什么結(jié)果:
#-*-encoding:utf-8-*-
s=u"中".encode(encoding='utf-8')
print("s:",s)
print("s==中?",s=='中')
print("typeofs:",type(s))
print("str==bytes?",bytes==type(s))
輸出:
('s:','\xe4\xb8\xad')
('s==\xe4\xb8\xad?',True)
('typeofs:',)
('str==bytes?',True)
從輸出的結(jié)果可以得到:
-編碼得到的對(duì)象跟我們直接定義的字符串是一樣的,都是str
-str就是bytes(python中)
2.1.2.解決方法
UnicodeEncodeError是說(shuō)編碼方法支持的字符不全,而UTF-8編碼就能很好地對(duì)Unicode編碼,所以只要把編碼方法指定為utf-8就可以了。
在python2中:
如果你調(diào)用encode方法但沒(méi)有指定encoding參數(shù),那很可能使用了系統(tǒng)默認(rèn)的參數(shù),就像:
#-*-encoding:utf-8-*-
importsys
print"defaultencodingis%s."%sys.getdefaultencoding()
u'中'.encode()
輸出:
defaultencodingisascii.
UnicodeEncodeError:'ascii'codeccan'tencodecharacteru'\u4e2d'inposition0:ordinalnotinrange(128)
可以手動(dòng)指定encoding參數(shù),也可以修改python默認(rèn)編碼方法:
#-*-encoding:utf-8-*-
importsys
reload(sys)
sys.setdefaultencoding('utf-8')#必須在reload之后調(diào)用
printu'中'.encode()
在python3中:
在python3中你很難看到UnicodeEncodeError了,因?yàn)閜ython3的默認(rèn)編碼就是utf-8,而Unicode字符都可以用utf-8編碼方法編碼。
2.2.編碼錯(cuò)誤UnicodeDecodeError
2.2.1.錯(cuò)誤分析
導(dǎo)致該錯(cuò)誤的原因是使用了錯(cuò)誤的解碼方式把字節(jié)數(shù)據(jù)還原成字符。例如在工作中,有一個(gè)utf-8生成中文文檔,我們選擇用ascii編碼解碼,就會(huì)報(bào)這個(gè)錯(cuò)。
錯(cuò)誤復(fù)現(xiàn):
我們知道python中字符串和字節(jié)是一樣,我們可以定義一個(gè)中文字符串,通過(guò)ascii來(lái)解碼生成Unicode,復(fù)現(xiàn)這個(gè)錯(cuò)誤:
#-*-encoding:utf-8-*-
print'中'.decode(encoding='ascii')
輸出:
UnicodeDecodeError:'ascii'codeccan'tdecodebyte0xe4inposition0:ordinalnotinrange(128)
注意,這是一個(gè)UnicodeDecodeError錯(cuò)誤,區(qū)別于編碼錯(cuò)誤UnicodeEncodeError,它提示無(wú)法把字節(jié)0xe4(實(shí)際上中對(duì)應(yīng)三字節(jié),0xe4是第一個(gè))解碼成Unicode。0xe4轉(zhuǎn)換為10進(jìn)制是228,已經(jīng)超過(guò)127了,所以ascii無(wú)能力了。
2.2.2.解決方法
我們需要知道生成字節(jié)的編碼方式才能進(jìn)行還原,就好像對(duì)一個(gè)文件進(jìn)行了加密,必須得知道加密的密碼才能把文件還原,而解碼方式(或者稱(chēng)為編碼)就是那個(gè)密碼。所以不管是python2還是python3直接去修改默認(rèn)編碼為UTF-8不一定能解決問(wèn)題,具體的方法有兩種:
通過(guò)源碼找到解碼失敗的字節(jié)是使用那種編碼生成的
對(duì)報(bào)錯(cuò)的字節(jié)數(shù)據(jù)使用各種常見(jiàn)的編碼進(jìn)行解碼,觀察哪一種是正確的
以一個(gè)例子來(lái)說(shuō)明為什么直接設(shè)置默認(rèn)編碼為UTF-8不能有效解決UnicodeDecodeError問(wèn)題
python文件頭指定了編碼為,在聲明字符串時(shí)候?qū)?huì)使用指定的編碼轉(zhuǎn)換為字節(jié):
#-*-encoding:GBK-*-
s="張"
print("sis",s)
s_unicode=u'張'
print("encodingwithGBKis",s_unicode.encode("GBK"))
print("encodingwithUTF-8is",s_unicode.encode("UTF-8"))
輸出:
('sis','\xd5\xc5')
('encodingwithGBKis','\xd5\xc5')
('encodingwithUTF-8is','\xe5\xbc\xa0')
在文件頭中指定了GBK編碼后默認(rèn)情況下字符張就會(huì)被編碼為\xd5\xc5,這與我們手動(dòng)用GBK編碼得到結(jié)果一致,而使用utf-8編碼得到的是3個(gè)字節(jié)的數(shù)據(jù)\xe5\xbc\xa0(使用了更多的存儲(chǔ)空間)。
例子開(kāi)始了,在python2中將一個(gè)dict轉(zhuǎn)換成json:
#-*-encoding:GBK-*-
importjson
d={'name':"張",'sex':u'man'}
print(json.dumps(d))
輸出:
UnicodeDecodeError:'utf8'codeccan'tdecodebyte0xd5inposition0:invalidcontinuationbyte
錯(cuò)誤說(shuō)無(wú)法使用utf-8解碼0xd5,0xd5也就是GBK中的張,我們知道這個(gè)字節(jié)是用GBK生成的,這個(gè)時(shí)候可以設(shè)置json.dumps的encoding參數(shù)解決:
#-*-encoding:GBK-*-
importjson
d={'name':"張",'sex':u'man'}
print(json.dumps(d,encoding='gbk'))
修改一下代碼,繼續(xù)使用我們熟悉的utf-8編碼來(lái)執(zhí)行:
#-*-encoding:utf-8-*-
importjson
d={'name':"張",'sex':u'man'}
print(json.dumps(d,encoding='utf-8'))
輸出:
{"name":"\u5f20","sex":"man"}
發(fā)現(xiàn)name是unicode,使用ensure_ascii=False,不強(qiáng)制轉(zhuǎn)換成ascii:
#-*-encoding:utf-8-*-
importjson
d={'name':"張",'sex':u'man'}
print(json.dumps(d,encoding='utf-8',ensure_ascii=False))
輸出:
File"/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py",line251,indumps
sort_keys=sort_keys,**kw).encode(obj)
File"/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py",line210,inencode
return''.join(chunks)
UnicodeDecodeError:'ascii'codeccan'tdecodebyte0xe5inposition1:ordinalnotinrange(128)
納尼,文件頭也是utf-8,也指定了utf-8怎么還是報(bào)錯(cuò)?錯(cuò)誤提示用ascii去解碼0xe5字節(jié),在上面的代碼輸出中可以知道0xe5是utf-8對(duì)字符張編碼的第一個(gè)字節(jié),報(bào)錯(cuò)的原因是用ascii去解析utf-8生成的字節(jié)了。我們并沒(méi)有設(shè)置哪個(gè)地方使用ascii解碼,應(yīng)該是系統(tǒng)默認(rèn)的編碼,嘗試設(shè)置系統(tǒng)默認(rèn)編碼再執(zhí)行:
#-*-encoding:utf-8-*-
importsys
reload(sys)
sys.setdefaultencoding('utf-8')
importjson
d={'name':"張",'sex':u'man'}
print(json.dumps(d,encoding='utf-8',ensure_ascii=False))
輸出:
{"name":"張","sex":"man"}
3.總結(jié)
3.1.python2和python3對(duì)字符串的處理區(qū)別
Python2
默認(rèn)編碼是ascii
Python3
importsys;sys.setdefaultencoding('utf-8')
3.2.為什么不全用UTF-8編碼?
UTF-8包含的字符更多,占用的內(nèi)存和磁盤(pán)空間也更大,比如對(duì)漢字張,utf-8是3個(gè)字節(jié),gbk是2個(gè)字節(jié)。
3.3.如何快速解決UnicodeEncodeError錯(cuò)誤?
python3中基本不會(huì)出現(xiàn),python2中嘗試設(shè)置默認(rèn)編碼為utf-8。
3.4.快速解決UnicodeDecodeError?
需要知道出錯(cuò)的字節(jié)是使用哪種編碼方式生成的,然后嘗試把默認(rèn)編碼設(shè)置成這種。
以上內(nèi)容為大家介紹了解決Python編碼問(wèn)題,希望對(duì)大家有所幫助,如果想要了解更多Python相關(guān)知識(shí),請(qǐng)關(guān)注IT培訓(xùn)機(jī)構(gòu):千鋒教育。http://m.2667701.com/