python bytes和bytearray、編碼和解碼

str、bytes和bytearray簡介

str是字符數據,bytes和bytearray是字節數據。它們都是序列,能夠進行迭代遍歷。str和bytes是不可變序列,bytearray是可變序列,能夠原處修改字節。html

bytes和bytearray都能使用str類型的通用函數,好比find()、replace()、islower()等,不能用的是str的格式化操做。因此,若有須要,參考字符串(string)方法整理來獲取這些函數的使用方法。python

str

str將各個字符組合在一塊兒,以一種不可變序列進行存儲。可是在底層它仍是一個個的二進制數,是由一個個的字節組成的(也就是byte),只不過python根據指定的字符集編碼"強行"將字節序列顯示爲字符。編輯器

python 3.x中默認str是unicode格式編碼的,例如UTF-8字符集。ide

>>> import sys
>>> sys.getdefaultencoding()
'utf-8'

unicode編碼的str,意味着可以直接存儲除ascii碼外的不少字符,好比中文,好比歐洲的重音符號。還意味着能夠將一個unicode字符存儲爲多個字節,並將連續多個的字節翻譯成單個對應的字符。函數

>>> a = "我"
>>> a
'我'

>>> ord(a)
25105

>>> a.encode()
b'\xe6\x88\x91'

根據指定字符集,底層的字節序列和字符序列間的轉換過程徹底無需人爲的參與,python已經作好了一切。工具

bytes

bytes是不可變的二進制格式字節數據(注意,是字節不是字符),以整數方式表示。例如對於ascii範圍內的字符"a",它存儲爲97。編碼

要構造bytes類型的數據,方法之一是在字符串前面加上b或B前綴。翻譯

例如:設計

>>> B = b"abcd"
>>> [i for i in B]
[97, 98, 99, 100]

>>> B[0] = "A"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment

bytes和下面的bytearray都能使用str類型的絕大部分方法。例如find()、replace()等,但用法上可能會有所區別,好比str.replace()的替換參數期待的是字符,而bytes.replace()的替換參數多是字節。例如:code

>>> b'abcd'.replace(b'cd',b'XY')
b'abXY'

bytearray

bytearray是可變的二進制數據(byte)

要構造bytearray對象,方法之一是將bytes數據做爲bytearray()方法的參數,或者將str數據和編碼做爲參數。

例如:

>>> S = b"abcd"
>>> BA = bytearray(S)

>>> [ i for i in BA ]
[97, 98, 99, 100]

>>> BA[0] = 65
>>> BA
bytearray(b'Abcd')

unicode字符

單字節的字符(8bit位,共256個字符,ascii只用到了7個字節)能表示出來的字符畢竟有限,例如它無法表示出中文字符。

因此,各國設計了各類多字節的字符編碼來表達本身國家的文字,底層仍然使用二進制數存儲,而後經過設計好的編碼表將二進制數轉換成各類字符。好比中國有GBK的各類編碼,還有全球通用的編碼類型unicode、utf-八、utf-16等。

不管什麼編碼,內部都包含ascii編碼(也有例外,好比utf-16),它只需單個字節。也就是說,ascii編碼是任何其它編碼表的子集。但有些編碼表強制規定每一個字符佔多少個字節(好比unicode固定爲2個字節),有些編碼表動態決定每一個字符佔多少個字節(好比utf-8是變長的,可能佔用1-4個字節空間,存儲字母爲1個字節,存儲中文字符爲3個字節)。

關於unicode和utf-X格式的編碼關係,粗略地能夠認爲utf-X是unicode格式的一種特殊類型。實際上在存儲utf數據時,內部會自動在Unicode和utf之間進行轉換。

要構建Unicode類型,只需加上u或U前綴。

>>> U = u"我愛你"

>>> B = bytes(U,"utf-8")
>>> B
b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0'

>>> BA = bytearray(U,"utf-8")
>>> BA
bytearray(b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0')

編碼和解碼

下面一張圖搞懂編碼、解碼、編碼表之間的關係。

不難看出,它們是一種根據編碼表進行翻譯、映射的過程:

編碼:str   --> bytes
解碼:bytes --> str

實際上,字符串類型只有encode()方法,沒有decode()方法,而bytes類型只有decode()方法而沒有encode()方法。

>>> set( dir(str) ) - set( dir(bytes) )
{'encode', ... , 'isidentifier', 'format'}

>>> set( dir(bytes) ) - set( dir(str) )
{'decode', 'hex', 'fromhex'}

二進制格式的數據也常稱爲裸數據(raw data),因此str數據通過編碼後獲得raw data,raw data解碼後獲得的str。

python中的編碼、解碼

上面說了,編碼是將字符數據轉換成字節數據(raw data),解碼是將字節數據轉換成字符數據。在Python中字符數據也就是字符串,即str類型,字節數據也就是bytes類型或bytearray類型。

編碼時,可使用字節類型的構造方法bytes()、bytearray()來構造字節,也可使用str類型的encode()方法來轉換

解碼時,可使用str類型的構造方法str()來構造字符串,也可使用bytes、bytearray()類型的decode()方法

另外須要注意的是,編碼和解碼的過程當中都須要指定編碼表(字符集),默認採用的是utf-8字符集。

編碼過程

例如,使用encode()的方式將str編碼爲bytes數據。

>>> str1 = "abcd"
>>> str2 = "我愛你"

# 默認編碼
>>> str1.encode()
b'abcd'
>>> str2.encode()
b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0'

# 顯式指定使用utf-8進行編碼
>>> str1.encode("utf-8")
b'abcd'
>>> str2.encode("utf-8")
b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0'

# 使用utf-16編碼
>>> str1.encode("utf-16")
b'\xff\xfea\x00b\x00c\x00d\x00'
>>> str2.encode("utf-16")
b'\xff\xfe\x11b1r`O'

# 使用gb2312編碼
>>> str1.encode("gb2312")
b'abcd'
>>> str2.encode("gb2312")
b'\xce\xd2\xb0\xae\xc4\xe3'

# 使用gbk編碼
>>> str1.encode("gbk")
b'abcd'
>>> str2.encode("gbk")
b'\xce\xd2\xb0\xae\xc4\xe3'

使用bytes()和bytearray()將str構形成bytes或bytearray數據,這兩個方法都要求str->byte的過程當中給定編碼。

>>> bytes(str1,encoding="utf-8")
b'abcd'
>>> bytes(str1,encoding="utf-16")
b'\xff\xfea\x00b\x00c\x00d\x00'

>>> bytearray(str1,encoding="utf-8")
bytearray(b'abcd')
>>> bytearray(str2,encoding="utf-8")
bytearray(b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0')

實際上,bytes()、bytearray()這兩個方法構造字節數據的時候還有點複雜,由於能夠從多個數據源來構造,好比字符串、整數值、buffer。如何使用這兩個方法構造字節數據,詳細內容參考help(bytes)help(bytearray)給出的說明,這裏給幾個簡單示例。

構造bytes的方式:

# 構造空bytes對象
>>> bytes()
b''

# 使用str構造bytes序列,須要指定編碼
>>> bytes("abcd",encoding="utf-8")
b'abcd'

# 使用int初始化5個字節的bytes序列
>>> bytes(5)
b'\x00\x00\x00\x00\x00'

# 使用可迭代的int序列構造字節序列
# int值必須爲0-256之內的數
>>> bytes([65,66,67])
b'ABC'

# 使用bytes或buffer來構造bytes對象
>>> bytes(b'abcd')
b'abcd'

構造bytearray的方式:

# 構造空bytearray對象
>>> bytearray()
bytearray(b'')

# 使用bytes或buffer構造bytearray序列
>>> bytearray(b"abcd")
bytearray(b'abcd')

# 使用str構造bytearray序列,須要指定編碼
>>> bytearray("abcd",encoding="utf-8")
bytearray(b'abcd')

# 使用int初始化5個字節的bytearray序列
>>> bytearray(5)
bytearray(b'\x00\x00\x00\x00\x00')

# 使用可迭代的int序列構造bytearray序列
# int值必須爲0-256之內的數
>>> bytearray([65,66,67])
bytearray(b'ABC')

解碼過程

解碼是字節序列到str類型的轉換。

例如,使用decode()方法進行解碼"我"字,它的utf-8的編碼對應爲"\xe6\x88\x91":

>>> b = b'\xe6\x88\x91'

# 採用默認字符集utf-8
>>> b.decode()
'我'

# 顯式指定編碼表
>>> b.decode("utf-8")
'我'

使用str()進行轉換。

>>> str(b,"utf-8")
'我'

關於亂碼

當編碼、解碼的過程使用了不一樣的(不兼容的)編碼表時,就會出現亂碼。因此,解決亂碼的惟一方式是指定對應的編碼表進行編碼、解碼。

例如,使用utf-8編碼"我"字,獲得一個bytes序列,而後使用gbk解碼這個bytes序列。

>>> "我".encode().decode("gbk")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0x91 in position 2: incomplete multibyte sequence

這裏報錯了,由於utf-8的字節序列裏有gbk沒法解碼的字節。若是使用文本編輯器同樣的工具去顯化這個過程,獲得的將是亂碼字符。

相關文章
相關標籤/搜索