文本和字節序列


1 字符問題
“字符串”是個相當簡單的概念:一個字符串是一個字符序列。問題出在“字符”的定義上。
在 2015 年,“字符”的最佳定義是 Unicode 字符。因此,從 Python 3 的str 對象中獲取的元素是 Unicode 字符,這相當於從 Python 2 的unicode 對象中獲取的元素,而不是從 Python 2 的 str 對象中獲取的原始字節序列。
Unicode 標准把字符的標識和具體的字節表述進行了如下的明確區分。
  
字符的標識,即碼位,是 0~1 114 111 的數字(十進制),在Unicode 標准中以 4~6 個十六進制數字表示,而且加前綴“U+”。例如,字母 A 的碼位是 U+0041,歐元符號的碼位是 U+20AC,高音譜號的碼位是 U+1D11E。在 Unicode 6.3 中(這是 Python 3.4 使用的標准),約 10% 的有效碼位有對應的字符。
字符的具體表述取決於所用的編碼。編碼是在碼位和字節序列之間轉換時使用的算法。在 UTF-8 編碼中,A(U+0041)的碼位編碼成單個字節 \x41,而在 UTF-16LE 編碼中編碼成兩個字節\x41\x00。再舉個例子,歐元符號(U+20AC)在 UTF-8 編碼中是三個字節——\xe2\x82\xac,而在 UTF-16LE 中編碼成兩個字節:\xac\x20。
把碼位轉換成字節序列的過程是編碼;把字節序列轉換成碼位的過程是解碼。示例 4-1 闡釋了這一區分。
>>> s = 'café'
>>> len(s) #
4
>>> b = s.encode('utf8') #
>>> b
b'caf\xc3\xa9' #
>>> len(b) #
5
>>> b.decode('utf8') #
'café
❶ 'café' 字符串有 4 個 Unicode 字符。
❷ 使用 UTF-8 把 str 對象編碼成 bytes 對象。
❸ bytes 字面量以 b 開頭。
❹ 字節序列 b 有 5 個字節(在 UTF-8 中,“é”的碼位編碼成兩個字節)。
❺ 使用 UTF-8 把 bytes 對象解碼成 str 對象。
如果想幫助自己記住 .decode() 和 .encode() 的區別,可以把字節序列想成晦澀難懂的機器磁芯轉儲,把 Unicode 字符串想成“人類可讀”的文本。那么,把字節序列變成人類可讀的文本字符串就是解碼,而把字符串變成用於存儲或傳輸的字節序列就是編碼。
雖然 Python 3 的 str 類型基本相當於 Python 2 的 unicode 類型,只不過是換了個新名稱,但是 Python 3 的 bytes 類型卻不是把 str 類型換個名稱那么簡單,而且還有關系緊密的 bytearray 類型。因此,在討論編碼和解碼的問題之前,有必要先來介紹一下二進制序列類型。
 
2 字節概要
新的二進制序列類型在很多方面與 Python 2 的 str 類型不同。首先要知道,Python 內置了兩種基本的二進制序列類型:Python 3 引入的不可變bytes 類型和 Python 2.6 添加的可變 bytearray 類型。(Python 2.6 也引入了 bytes 類型,但那只不過是 str 類型的別名,與 Python 3 的bytes 類型不同。)
>>> cafe = bytes('café', encoding='utf_8') ➊
>>> cafe
b'caf\xc3\xa9'
>>> cafe[0] ➋
99
>>> cafe[:1] ➌
b'c'
>>> cafe_arr = bytearray(cafe)
>>> cafe_arr ➍
bytearray(b'caf\xc3\xa9')
>>> cafe_arr[-1:] ➎
bytearray(b'\xa9')
❶ bytes 對象可以從 str 對象使用給定的編碼構建。
❷ 各個元素是 range(256) 內的整數。
❸ bytes 對象的切片還是 bytes 對象,即使是只有一個字節的切片。
❹ bytearray 對象沒有字面量句法,而是以 bytearray() 和字節序列字面量參數的形式顯示。
❺ bytearray 對象的切片還是 bytearray 對象。
my_bytes[0] 獲取的是一個整數,而 my_bytes[:1] 返回的是一個長度為 1 的 bytes 對象——這一點應該不會讓人意外。s[0] == s[:1] 只對 str 這個序列類型成立。不過,str 類型的這個行為十分罕見。對其他各個序列類型來說,s[i] 返回一個元素,而 s[i:i+1] 返回一個相同類型的序列,里面是 s[i] 元素。

二進制序列有個類方法是 str 沒有的,名為 fromhex,它的作用是解析十六進制數字對(數字對之間的空格是可選的),構建二進制序列:
>>> bytes.fromhex('31 4B CE A9')
b'1K\xce\xa9'
構建 bytes 或 bytearray 實例還可以調用各自的構造方法,傳入下述參數。
一個 str 對象和一個 encoding 關鍵字參數。
一個可迭代對象,提供 0~255 之間的數值。
一個整數,使用空字節創建對應長度的二進制序列。[Python 3.5 會把這個構造方法標記為“過時的”,Python 3.6 會將其刪除。參見“PEP 467—Minor API improvements for binarysequences”(https://www.python.org/dev/peps/pep-0467/)。]
一個實現了緩沖協議的對象(如bytes、bytearray、memoryview、array.array);此時,把源對象中的字節序列復制到新建的二進制序列中。
 
結構體和內存視圖
struct 模塊提供了一些函數,把打包的字節序列轉換成不同類型字段組成的元組,還有一些函數用於執行反向轉換,把元組轉換成打包的字節序列。struct 模塊能處理 bytes、bytearray 和 memoryview 對象。
memoryview 類不是用於創建或存儲字節序列的,而是共享內存,讓你訪問其他二進制序列、打包的數組和緩沖中的數據切片,而無需復制字節序列,例如 Python Imaging Library(PIL) 就是這樣處理圖像的。
  示例 4-4 展示了如何使用 memoryview 和 struct 提取一個 GIF 圖像的寬度和高度。
>>> import struct
>>> fmt = '<3s3sHH' #
>>> with open('filter.gif', 'rb') as fp:
... img = memoryview(fp.read()) #
...
>>> header = img[:10] #
>>> bytes(header) #
b'GIF89a+\x02\xe6\x00'
>>> struct.unpack(fmt, header) #
(b'GIF', b'89a', 555, 230)
>>> del header #
>>> del img
❶ 結構體的格式:< 是小字節序,3s3s 是兩個 3 字節序列,HH 是兩個16 位二進制整數。
❷ 使用內存中的文件內容創建一個 memoryview 對象……
❸ ……然后使用它的切片再創建一個 memoryview 對象;這里不會復制字節序列。
❹ 轉換成字節序列,這只是為了顯示;這里復制了 10 字節。
❺ 拆包 memoryview 對象,得到一個元組,包含類型、版本、寬度和高度。
❻ 刪除引用,釋放 memoryview 實例所占的內存。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM