在《不可不知的Python字符編碼使用技巧(上)》中,我們講清楚了字符編碼的基礎(chǔ)概念,我相信由此再來(lái)介紹python中的字符編碼就會(huì)容易的多。
通過(guò)上一集我們知道,ASCII碼(包括其最常見(jiàn)的超集Latin-1)依賴這樣的一個(gè)假設(shè),即每一個(gè)字符與一個(gè)字節(jié)相匹配,由于存在太多的字符,因此不可避免的會(huì)出現(xiàn)問(wèn)題,Unicode字符集通過(guò)使用4個(gè)字節(jié)來(lái)表示1個(gè)字符,則解決了該問(wèn)題。
Python中有兩種字符串:文本字符串和字節(jié)字符串。其中文本字符串類(lèi)型被命名為str,內(nèi)部采用Unicode字符集(兼容ASCII碼),而字節(jié)字符串則直接用來(lái)表示原始的字節(jié)序列(用print函數(shù)來(lái)打印字節(jié)字符串時(shí),若字節(jié)在ascii碼范圍內(nèi),則顯示為ascii碼對(duì)應(yīng)的字符,其余的則直接顯示為16進(jìn)制數(shù)),該類(lèi)型被命名為bytes。
看一個(gè)簡(jiǎn)單的例子:
代碼片段:
s = 'apple'
b = b'apple'
print(b)
print(type(b))
print(s)
print(type(s))
運(yùn)行結(jié)果:
b'apple'
class 'bytes'>
apple
class 'str'>
再近距離的看看bytes類(lèi)型字節(jié)字符串,本質(zhì)上它就是一串單字節(jié)16進(jìn)制數(shù)
代碼片段:
b = b'apple'
print(b[0])
print(b[1:])
print(list(b))
運(yùn)行結(jié)果:
97
b'pple'
[97, 112, 112, 108, 101]
上一小節(jié)介紹的Python的兩種字符串,和編碼、解碼有何關(guān)聯(lián)呢?其實(shí)從本質(zhì)上來(lái)說(shuō),編碼和解碼就是str和bytes這兩種字符串類(lèi)型之間的互相轉(zhuǎn)換。
str包含一個(gè)encode方法,使用特定編碼將該字符串轉(zhuǎn)換為一個(gè)bytes,這稱(chēng)之為編碼。bytes類(lèi)包含了一個(gè)decode方法,也接受一個(gè)編碼作為單個(gè)必要參數(shù),并返回一個(gè)str,這稱(chēng)之為解碼。這種轉(zhuǎn)換操作是顯式的操作,且必須根據(jù)數(shù)據(jù)被編碼時(shí)采用的編碼類(lèi)型進(jìn)行解碼。
首先說(shuō)說(shuō)編碼,即將unicode的str文本字符串轉(zhuǎn)換為bytes的字節(jié)字符串,可以顯式的傳入指定編碼(一般來(lái)說(shuō)采用utf-8編碼),或使用平臺(tái)的默認(rèn)編碼。
代碼片段:
s = 'π排球の'
b1 = s.encode('utf-8')
b2 = s.encode()
print(b1)
print(b2)
運(yùn)行結(jié)果:
b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
b'\xcf\x80\xe6\x8e\x92\xe7\x90\x83\xe3\x81\xae'
那么我們看看,在不寫(xiě)編碼的時(shí)候,平臺(tái)默認(rèn)的編碼方式到底是什么
代碼片段:
import sys
print(sys.platform)
print(sys.getdefaultencoding())
運(yùn)行結(jié)果:
win32
utf-8
可以看出我這個(gè)平臺(tái)默認(rèn)選擇的是utf-8編碼方式。
接下來(lái)我們來(lái)比較一下unicode、latin-1、ASCII編碼方式的兼容性問(wèn)題:
首先,非ASCII字符無(wú)法使用ASCII編碼轉(zhuǎn)換成字節(jié)字符串
代碼片段:
s = 'π排球の'
b = s.encode('ascii')
運(yùn)行結(jié)果:
Traceback (most recent call last):
File 'E:/12homework/12homework.py', line 2, in module>
b = s.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3:
ordinal not in range(128)
其次,Latin-1和unicode編碼方式不兼容。
例如,重音字符會(huì)在latin-1字符集和unicode字符集中同時(shí)存在,但是通過(guò)latin-1和unicode編碼方式編出來(lái)的字節(jié)流是不一樣的,注意,雖然unicode字符集是包含了latin-1字符集,但是不代表utf-8編碼方式兼容latin-1編碼方式。因?yàn)閡nicode字符集中除了ascii字符集外,都是采用多字節(jié)的編碼方式,而latin-1一律采用單字節(jié)的方式
代碼片段:
s = '?è'
print(s.encode('utf-8'))
print(s.encode('latin-1'))
運(yùn)行結(jié)果:
b'\xc3\x84\xc3\xa8'
b'\xc4\xe8'
只有ascii字符集中的字符,三種編碼方式得到的結(jié)果才完全一致。對(duì)unicode進(jìn)行編碼的時(shí)候,針對(duì)常規(guī)的7位ASCII文本,由于utf-8以及l(fā)atin-1編碼方式都是兼容ASCII的,所以結(jié)果都是一樣的。
代碼片段:
s = 'abc'
print(s.encode('utf-8'))
print(s.encode('latin-1'))
print(s.encode('ascii'))
運(yùn)行結(jié)果:
b'abc'
b'abc'
b'abc'
那對(duì)應(yīng)的,再來(lái)談?wù)刣ecode解碼方法吧。
將bytes類(lèi)型字符串轉(zhuǎn)換成str類(lèi)型的unicode文本字符串也是一樣,要么指定編碼參數(shù),要么使用平臺(tái)的默認(rèn)參數(shù)。這個(gè)例子中,我們要操作的字節(jié)字符串b是通過(guò)utf-8編碼方式對(duì)文本字符串'π排球の'編碼而形成的。
代碼片段:
b = b'\xe6\x8e\x92\xe7\x90\x83'
s1 = b.decode(encoding='utf-8')
s2 = b.decode()
s3 = b.decode(encoding='latin-1')
print(s1)
print(s2)
print(s3)
運(yùn)行結(jié)果:
排球
排球
??’???
值得注意的是,最后一行代碼想通過(guò)latin-1解碼字節(jié)字符串,由于字節(jié)字符串是通過(guò)utf-8編碼形成,因此這樣解碼形成得到的只能是亂碼。
因?yàn)?,utf-8編碼是用兩個(gè)字節(jié)來(lái)表示非ASCII的高128字符,而latin-1則是用一個(gè)字節(jié)來(lái)一一對(duì)應(yīng)
聯(lián)系客服