bytes and bytearray
1 bytes和bytearray的介紹
bytes
和bytearray
是 python3中引入了兩個新型別bytes
是不可變的位元組序列bytearray
是可變的位元組陣列
記憶體中的資料都是二進位制的0和1,並且我們按照位元組的方式邏輯上將這些0和1分成一個一個的位元組(位元組仍然是二進位制的0和1),一個位元組佔 8 位,當一個位元組中的高位是 0, 就按照 ASCII解碼; 如果一個位元組的高位是1, 就不會按照 ASCII編碼表解碼。此時如果不指定編碼的方式(解碼的方式),計算機就不知道如何把這些數字翻譯成正確的字元,由於我們的計算機使用的是中文,那麼計算機就會預設將兩兩位元組組合起來去解碼,但是如果這些數當初不是按照兩兩位元組組合的中字符集編碼的,那麼解碼之後就是亂碼。
2 字串與bytes
- 字串是字元組成的有序序列,字元可以使用編碼來理解
- bytes是位元組組成的有序的不可變序列
- bytearray是位元組組成的有序的可變序列
3 編碼與解碼
字串
--> bytes(二進位制)
編碼 encode
bytes(二進位制)
--> 字串
解碼 decode
3.1 我們先來思考一個例子:
num = "1" 和 num = 1 在計算機中是怎樣的,我們使用文字方式開啟後又是什麼樣子?
字串 num = "1" 以文字方式顯示(也就是人類可讀)
此時的1就是我們人類可見的字串 1
字串 num = "1" 以二進位制方式顯示(也就是計算機可讀)
31 就是 ASCII編碼表中對應的字串1對應的 16 進位制數字
數字 num = 1 以二進位制方式顯示(也就是計算機可讀)
此時的1就是實際在計算機中存在的 16 進位制數字1
數字 num = 1 以文字方式顯示(也就是人類可讀)
SOH 就是 ASCII編碼表中 16 進位制數字對應的字串
通過上面的例子我們可以得出以下結論:
計算機只認得二進位制數字,二進位制的世界裡是一個個位元組,文字的世界裡是一個個字元。
字串要儲存在計算機裡面:
查表去 --> 轉換為對應的是二進位制數字(也可以理解為16進位制數字) --> 儲存
要將二計算進中存貯的二進位制數字以人類可讀方式檢視(比如通過文字編輯器):
查表去 --> 轉換為二進位制數字(也可以理解為16進位制數字)對應的字元 --> 顯示
3.2 encode & decode
字串按照不同的字符集編碼encode返回位元組序列bytes
encode(encoding='utf-8', errors='strict') -> bytes
--------------------------------------------------------------------------------------
In [9]: '中國'.encode(encoding='utf-8', errors='strict')
Out[9]: b'\xe4\xb8\xad\xe5\x9b\xbd'
位元組序列按照不同的字符集解碼decode返回字串
bytes.decode(encoding="utf-8", errors="strict") -> str
bytearray.decode(encoding="utf-8", errors="strict") -> str
--------------------------------------------------------------------------------------
In [12]: b'\xe4\xb8\xad\xe5\x9b\xbd'.decode(encoding="utf-8", errors="strict")
Out[12]: '中國'
編碼和解碼的演示
位元組的世界裡沒有編碼,字串的世界裡才有編碼
# 編碼的過程:我們將字串“中國”編碼為二進位制的bytes
In [1]: "中國".encode() # 預設編碼使用的是 utf-8 字符集
Out[1]: b'\xe4\xb8\xad\xe5\x9b\xbd' # 返回的結果中前面有一個'b',說明這不是一個簡單的字串,而是一個bytes(位元組)
# 解碼的過程:我們將二進位制的bytes解碼為字串
In [2]: "中國".encode().decode()
Out[2]: '中國'
如果編碼和解碼的時候使用的不是同一個字符集,那麼就會轉換失敗(使用文字剪輯器開啟的時候就是亂碼)
In [3]: "中國".encode().decode(encoding='gbk')
--------------------------------------------------------------------------------------
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-3-5784dfe3e469> in <module>
----> 1 "中國".encode().decode(encoding='gbk')
UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 2: illegal multibyte sequence
我們編碼和解碼使用相同的字符集,就是成功的
In [4]: "中國".encode(encoding='gbk').decode(encoding='gbk')
Out[4]: '中國'
此時我們再來看看使用utf-8和gbk兩種字符集對字串"中國"進行編碼後的結果有什麼不同
In [6]: "中國".encode(encoding='utf-8')
Out[6]: b'\xe4\xb8\xad\xe5\x9b\xbd' # utf-8字符集三個位元組對應一個漢字
In [7]: "中國".encode(encoding='gbk') # gbk 字符集兩個位元組對應一個漢字
Out[7]: b'\xd6\xd0\xb9\xfa'
4 ASCII 碼
ASCII(American Standard Code for Information Interchange,美國資訊交換標準程式碼)是基於拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,並等同於國際標準ISO/IEC 646。
5 bytes
5.1 bytes定義
bytes() # 空bytes
bytes(int) # 指定位元組的bytes,被 0 填充
bytes(iterable_of_ints) -> bytes # [0,255] 的int組成的可迭代物件
bytes(string, encoding[, errors]) -> bytes # 等價與 string,encode()
bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer # 從一個位元組序列或者buffer複製出一個新的不可變的bytes物件
使用 b 字首定義
只允許基於ASCII使用字元形式 b'scm7'
使用16進製表示 b'\x73\x63\x6d\x37'
bytes()
In [13]: s = bytes()
In [14]: s
Out[14]: b''
In [17]: s = bytes(0)
In [18]: s
Out[18]: b''
bytes(int)
In [15]: s = bytes(5)
In [16]: s
Out[16]: b'\x00\x00\x00\x00\x00' # 由於無法顯示ASCII中16進位制對應的字元,只能用16進位制數字直接顯示
# bytes型別是不可變的
In [19]: s = bytes(5)
In [20]: s
Out[20]: b'\x00\x00\x00\x00\x00' # 注意前面有 'b',說明這是一個bytes,而不是普通的字串
In [21]: s[1] = 10
--------------------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-21-d8b471273ca8> in <module>
----> 1 s[1] = 10
TypeError: 'bytes' object does not support item assignment
bytes型別是可以解碼的
In [22]: s.decode()
Out[22]: '\x00\x00\x00\x00\x00' # 注意,前面的 'b' 不見了。說明已經轉為字串型別了(5個字元0)
bytes(iterable_of_ints)
返回bytes, [0,255] 的int組成的可迭代物件
In [23]: s = bytes(range(65,68)) # 65,66,67
In [24]: s
Out[24]: b'ABC'
In [25]: s = bytes(range(5)) # 0 1 2 3 4
In [26]: s
Out[26]: b'\x00\x01\x02\x03\x04' # 由於無法顯示ASCII中16進位制對應的字元,只能用16進位制數字直接顯示
使用 b 字首定義
In [27]: s = b'abc'
In [28]: s
Out[28]: b'abc'
string.encode()
In [30]: x = 'abc'.encode() # 效果和 b'abc' 是相同的
In [31]: x
Out[31]: b'abc'
bytes(string,encoding[,errors])
In [32]: bytes('abc','gbk')
Out[32]: b'abc'
bytes(bytes_or_buffer)
從一個位元組序列或者buffer複製出一個新的不可變的bytes物件
In [33]: s1 = b'xxx'
In [34]: s1
Out[34]: b'xxx'
In [35]: s2 = bytes(s1) # 記憶體拷貝
In [36]: s2
Out[36]: b'xxx'
檢驗一下對前面的理解
下面的結果是什麼?
s3 = b'\x41\x42\x43'
s3.decode()
5.2 bytes操作
bytes和str型別類似,都是不可變型別,所以方法很多都一樣。只不過bytes的方法,輸入是bytes,輸出是bytes
replace() 位元組的替換
b'abcdef'.replace(b'f',b'k')
b'abc'.find(b'b')
--------------------------------------------------------------------------------------
>>> b'abcdef'.replace(b'f',b'k')
'abcdek'
find() 位元組的查詢
找到返回索引值,否則返回 -1
b'abc'.find(b'b')
--------------------------------------------------------------------------------------
>>> b'abc'.find(b'a')
0
>>> b'abc'.find(b'b')
1
>>> b'abc'.find(b'c')
2
>>> b'abc'.find(b'd')
-1 # 找不到返回 -1
bytes.fromhex(string) 返回的是bytes,string是16進位制字串表示法
類方法,從16進位制字串構建bytes
string必須是2個字元的16進位制的形式,'6162 6a 6b',空格將被忽略
bytes.fromhex('6162 09 41 42 43')
--------------------------------------------------------------------------------------
In [41]: bytes.fromhex('6162 09 41 42 43')
Out[41]: b'ab\tABC'
# 其實這裡我們可以想到,一個大的數字可以轉變為多個位元組(bytes序列)
# 同時多個位元組也可以用來描述一個較大的數字
hex() 和 bytes.fromhex(string)是相反的,返回的是字串
返回16進製表示法表示的字串,相當於 直接取出來了記憶體中的資料,並顯示
'abc'.encode().hex()
--------------------------------------------------------------------------------------
print('abc'.encode().hex()) -> 616263
In [60]: bytes.fromhex('616163')
Out[60]: b'aac'
In [61]: b'abc'.hex()
Out[61]: '616263'
索引
返回該位元組對應的數,int型別
b'abcdef'[2]
--------------------------------------------------------------------------------------
In [62]: b'abcdef'[2] # ASCII 表中 c 對應的十進位制數就是 99
Out[62]: 99
In [63]: str(b'abcdef'[2])
Out[63]: '99'
6 bytearray
6.1 bytearray定義
bytearray() # 空bytearray
bytearray(int) # 指定位元組的bytearray,被 0 填充
bytearray(iterable_of_ints) -> bytearray # [0, 255]的int組成的可嗲帶物件
bytearray(string, encoding [,errors]) -> bytearray # 近似string.encode(),不過返回可變物件
bytearray(bytes_or_buffer) # 從一個位元組序列或這buffer複製出一個新的可變的bytearray物件
注意:
b
字首定義的型別是bytes型別,是不可變的
bytearray
字首定義的型別是bytearray型別,是可變的
6.2 bytearray操作
bytearray 的操作方法和byte型別的操作方法相同
replace() 替換
bytearray(b'abcedf').replace(b'f',b'k')
--------------------------------------------------------------------------------------
In [64]: bytearray(b'abcedf').replace(b'f',b'k')
Out[64]: bytearray(b'abcedk')
bytearray.fromhex(string)
類方法,string必須是2個字元的16進位制的形式,'6162 6a 6b',空格將被忽略
bytearray.fromhex('6162 09 41 4231')
--------------------------------------------------------------------------------------
In [69]: bytearray.fromhex('6162 09 41 4231')
Out[69]: bytearray(b'ab\tAB1')
hex()
返回十六進位制表示的字串
bytearray('abc'.encode()).hex()
--------------------------------------------------------------------------------------
print(bytearray('abc'.encode()).hex()) -> 616263
索引
返回該位元組對應的數,int型別
bytearray(b'abcdef')[2]
--------------------------------------------------------------------------------------
In [70]: bytearray(b'abcdef')[2]
Out[70]: 99 # ASCII 表中 c 對應的十進位制數就是 99
append()
b.append(item)
尾部追加一個元素
In [71]: b = bytearray()
In [72]: b.append(97)
In [73]: b
Out[73]: bytearray(b'a')
In [77]: b.append(98)
In [78]: b
Out[78]: bytearray(b'ab')
insert()
在指定的索引位置插入元素
b.insert(index, item)
In [79]: b.insert(1,99) # 在索引為1的位置插入 99 (b'c')
In [80]: b
Out[80]: bytearray(b'acb')
extend(iterable_of_ints)
b.extend(iterable_of_ints)
將一個可迭代的整數集合追加到當前bytearray
In [100]: b = bytearray()
In [101]: b
Out[101]: bytearray(b'')
In [102]: b.extend(range(65,67))
In [103]: b
Out[103]: bytearray(b'AB')
In [104]: b.extend(range(97,110))
In [105]: b
Out[105]: bytearray(b'ABabcdefghijklm')
pop(index=-1)
從指定的縮影上移除元素,預設是從尾部移除
# 預設是移除末尾的元素
In [106]: b
Out[106]: bytearray(b'ABabcdefghijklm')
In [107]: b.pop()
Out[107]: 109
In [108]: b
Out[108]: bytearray(b'ABabcdefghijkl') # 最後的m不見了
# 移除指定索引的元素
In [110]: b
Out[110]: bytearray(b'ABabcdefghijkl')
In [118]: b'ABabcdefghijkl'.hex()
Out[118]: '41426162636465666768696a6b6c'
In [111]: b.pop(-2)
Out[111]: 107
In [112]: b
Out[112]: bytearray(b'ABabcdefghijl') # k(\x6b == 107)沒有了
In [113]: b.pop(0)
Out[113]: 65
In [114]: b
Out[114]: bytearray(b'Babcdefghijl') # A(\x61 == 65)沒有了
remove(velue)
找到第一個value,並移除,找不到直接報異常 ValueErrror
b.remove(66)
注意:
上述方法都需要使用int型別,數值在[0,255]
reverse()
反轉bytearray,注意此操作是就地修改
In [120]: b
Out[120]: bytearray(b'Babcdefghijl')
In [121]:
In [121]: b.reverse()
In [122]: b
Out[122]: bytearray(b'ljihgfedcbaB')
clear()
清空bytearray
In [126]: b
Out[126]: bytearray(b'ljihgfedcbaB')
In [127]: b.clear()
In [128]: b
Out[128]: bytearray(b'')
7. int 和 bytes 互轉
int.from_bytes(bytes, byteorder)
將一個位元組陣列表示成整數
In [131]: i = int.from_bytes(b'abc','big') # big 指定是大頭位元組序,也就是 ---> 這樣的方向
In [132]: i
Out[132]: 6382179 # 十進位制表示的
In [133]: hex(i)
Out[133]: '0x616263 # 十六進位制表示的
int.to_bytes(length,byteorder)
byteorder: 位元組序
將一個整數表達成一個指定長度的位元組陣列
i = int.from_bytes(b'abc','big')
print(i,hex(i))
--------------------------------------------------------------------------------------
6382179 0x616263
print(i.to_bytes(3,'big'))
--------------------------------------------------------------------------------------
b'abc'
8. 思考題
記憶體中有一個數字。請問踏實什麼型別的?
我不知道,那要看你怎麼理解它,你要是把他當作是整形,就是int,
如果當作是字串,就要去查表解碼為字串,
如果你要是當他是 bytes理解,它就是一個個位元組
9. 總結
當我們開啟一段記憶體後,看到的是二進位制的數字,只有給定了資料型別後我們才知道他們表示的是什麼,
離開了資料型別,他們除了是0和1之外什麼都不是。
所以,資料一點要有型別。沒有型別我們將無法理解。