Effective Python(3)- 瞭解 bytes 與 str 的區別

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章