­

str、bytes和bytearray編碼

  • 2019 年 12 月 10 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/weixin_36670529/article/details/103453754

str是字元數據,bytes和bytearray是位元組數據。它們都是序列,可以進行迭代遍歷。str和bytes是不可變序列,bytearray是可變序列,可以原處修改位元組。

bytes和bytearray都能使用str類型的通用函數,比如find()、replace()、islower()等,不能用的是str的格式化操作。所以,如有需要,參考字元串(string)方法整理來獲取這些函數的使用方法。

str

str將各個字元組合在一起,以一種不可變序列進行存儲。但是在底層它還是一個個的二進位數,是由一個個的位元組組成的(也就是byte),只不過python根據指定的字符集編碼"強行"將位元組序列顯示為字元。

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

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

unicode編碼的str,意味著能夠直接存儲除ascii碼外的很多字元,比如中文,比如歐洲的重音符號。還意味著可以將一個unicode字元存儲為多個位元組,並將連續多個的位元組翻譯成單個對應的字元。

>>> a = "我"  >>> a  '我'    >>> ord(a)  25105    >>> a.encode()  b'xe6x88x91'

根據指定字符集,底層的位元組序列和字元序列間的轉換過程完全無需人為的參與,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()的替換參數可能是位元組。例如:

>>> 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-8、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'xe6x88x91xe7x88xb1xe4xbdxa0'  >>> BA = bytearray(U,"utf-8")  >>> BA  bytearray(b'xe6x88x91xe7x88xb1xe4xbdxa0')

編碼和解碼

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

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

編碼: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'xe6x88x91xe7x88xb1xe4xbdxa0'  # 顯式指定使用utf-8進行編碼  >>> str1.encode("utf-8")  b'abcd'  >>> str2.encode("utf-8")  b'xe6x88x91xe7x88xb1xe4xbdxa0'  # 使用utf-16編碼  >>> str1.encode("utf-16")  b'xffxfeax00bx00cx00dx00'  >>> str2.encode("utf-16")  b'xffxfex11b1r`O'  # 使用gb2312編碼  >>> str1.encode("gb2312")  b'abcd'  >>> str2.encode("gb2312")  b'xcexd2xb0xaexc4xe3'  # 使用gbk編碼  >>> str1.encode("gbk")  b'abcd'  >>> str2.encode("gbk")  b'xcexd2xb0xaexc4xe3'

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

>>> bytes(str1,encoding="utf-8")  b'abcd'  >>> bytes(str1,encoding="utf-16")  b'xffxfeax00bx00cx00dx00'  >>> bytearray(str1,encoding="utf-8")  bytearray(b'abcd')  >>> bytearray(str2,encoding="utf-8")  bytearray(b'xe6x88x91xe7x88xb1xe4xbdxa0')

實際上,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'x00x00x00x00x00'  # 使用可迭代的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'x00x00x00x00x00')  # 使用可迭代的int序列構造bytearray序列  # int值必須為0-256以內的數  >>> bytearray([65,66,67])  bytearray(b'ABC')

解碼過程

解碼是位元組序列到str類型的轉換。

例如,使用decode()方法進行解碼"我"字,它的utf-8的編碼對應為"xe6x88x91":

>>> b = b'xe6x88x91'  # 採用默認字符集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無法解碼的位元組。如果使用文本編輯器一樣的工具去顯化這個過程,得到的將是亂碼字元。