Python 有兩種型別可以表示字元序列
- bytes:例項包含的是原始資料,即 8 位的無符號值(通常按照 ASCII 編碼標準來顯示)
- str:例項包含的是 Unicode 碼點(code point,也叫作程式碼點),這些碼點與人類語言之中的文字字元相對應
a = b'h\x6511o' print(list(a)) print(a) a = 'a\\u300 propos' print(list(a)) print(a) # 輸出結果 [104, 101, 49, 49, 111] b'he11o' ['a', '\\', 'u', '3', '0', '0', ' ', 'p', 'r', 'o', 'p', 'o', 's'] a\u300 propos
Unicode 資料和二進位制資料轉換
- 把 Unicode 資料轉換成二進位制資料,必須呼叫 str 的 encode 方法(編碼)
- 把二進位制資料轉換成 Unicode 資料,必須呼叫 bytes 的 decode 方法(解碼)
- 呼叫這些方法時,可以明確指出字符集編碼,也可以採用系統預設的方案,通常是 UTF-8
使用原始的 8 位值與 Unicode 字串時需要注意的兩個問題
該問題等價於:使用 bytes 和 str 時需要注意的兩個問題
問題一:bytes 和 str 的例項互不相容
使用 + 操作符
# bytes+bytes print(b'a' + b'1') # str+str print('b' + '2') # 輸出結果 b'a1' b2
- bytes + bytes,str + str 都是允許的
- 但 bytes + str 會報錯
# bytes+str print('c' + b'2') # 輸出結果 print('c' + b'2') TypeError: can only concatenate str (not "bytes") to str
同型別之間也可以用二元操作符來比較大小
assert b'c' > b'a' assert 'c' > 'a'
但 bytes 和 str 之間用二元操作符也會報錯
assert b'c' > 'a' # 輸出結果 assert b'c' > 'a' TypeError: '>' not supported between instances of 'bytes' and 'str'
判斷 bytes 與 str 例項是否相等
兩個型別的例項相比較總會為 False,即使字元完全相同
# 判斷 str、bytes print('a' == b'a') # 輸出結果 False
格式化字串中的 %s
兩種型別的例項都可以出現在 % 操作符的右側,用來替換左側那個格式字串(format string)裡面的 %s
但是!如果格式字串是 bytes 型別,那麼不能用 str 例項來替換其中的 %s,因為 Python 不知道這個 str 應該按照什麼字符集來編碼
# % print(b'red %s' % 'blue') # 輸出結果 print(b'red %s' % 'blue') TypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'str'
但是!反過來卻可以,如果格式字串是 str 型別,則可以用bytes 例項來替換其中的 %s,但結果可能不是預期結果
# % print('red %s' % b'blue') # 輸出結果 red b'blue'
- 這樣會讓系統在 bytes 例項上面呼叫 __repr__ 方法
- 呼叫結果替換格式字串裡的 %s,因此程式會直接輸出 b'blue',而不是輸出 blue
問題二:操作檔案控制程式碼時需要使用 Unicode 字串操作
不能使用原始的 bytes
向檔案寫入二進位制資料會報錯
# 寫入二進位制資料 with open('test.txt', "w+") as f: f.write(b"\xf1\xf2") # 輸出結果 f.write(b"\xf1\xf2") TypeError: write() argument must be str, not bytes
- 報錯是因為 w 模式必須以文字模式寫入
- 將模式改成 wb 即可正常寫入二進位制資料
with open('test.txt', "wb") as f: f.write(b"\xf1\xf2")
讀取檔案中二進位制資料
with open('test.txt', "r+") as f: f.read() # 輸出結果 (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf1 in position 0: invalid continuation byte
- 報錯是因為 r 模式必須以文字模式讀取
- 以文字模式操縱檔案控制程式碼時,系統會採用預設的文字編碼方案處理二進位制資料
- 所以,上面那種寫法會讓系統通過 bytes.decode 把這份資料解碼成 str 字串,再用 str.encode 把字串編碼成二進位制值
- 然而對於大多數系統來說,預設的文字編碼方案是UTF-8,所以系統很可能會把 b'\xf1\xf2\xf3\xf4\xf5' 當成 UTF-8 格式的字串去解碼,於是就會出現上面那樣的錯誤
將模式改成 rb 即可正常讀取二進位制資料
with open('test.txt', "rb") as f: print(b"\xf1\xf2" == f.read()) # 輸出結果 True
另一種改法,設定 encoding 引數指定字串編碼
with open('test.txt', "r", encoding="cp1252") as f: print(f.read()) # 輸出結果 ñò
這樣也不會有異常了
重點
需要注意當前作業系統預設的字符集編碼
https://www.cnblogs.com/poloyy/p/15536156.html