struct
— 將位元組解釋為打包的二進位制資料¶
原始碼: Lib/struct.py
此模組在 Python 值和表示為 Python bytes
物件的 C 結構之間進行轉換。緊湊的 格式字串描述了與 Python 值之間的預期轉換。該模組的函式和物件可用於兩種截然不同的應用:與外部源(檔案或網路連線)進行資料交換,或者在 Python 應用程式和 C 層之間進行資料傳輸。
備註
如果未給出字首字元,則預設使用本機模式。它會根據構建 Python 直譯器的平臺和編譯器來打包或解包資料。打包的 C 結構的結果包括填充位元組,這些位元組會為涉及的 C 型別保留正確的對齊方式;同樣,解包時也會考慮對齊。相反,在與外部源通訊資料時,程式設計師負責定義位元組順序和元素之間的填充。有關詳細資訊,請參閱 位元組順序、大小和對齊。
幾個 struct
函式(以及 Struct
的方法)接受一個 buffer 引數。這指的是實現了 Buffer Protocol 並提供可讀或可讀寫緩衝區的物件。最常用於此目的的型別是 bytes
和 bytearray
,但許多其他可以被視為位元組陣列的物件也實現了緩衝區協議,以便它們可以被讀取/填充,而無需從 bytes
物件中進行額外的複製。
函式和異常¶
該模組定義了以下異常和函式
- exception struct.error¶
在各種情況下引發的異常;引數是描述問題所在的一個字串。
- struct.pack(format, v1, v2, ...)¶
返回一個包含值 v1, v2, ... 的 bytes 物件,這些值根據格式字串 format 進行打包。引數必須與格式所要求的數值完全匹配。
- struct.pack_into(format, buffer, offset, v1, v2, ...)¶
根據格式字串 format 打包值 v1, v2, ...,並將打包的位元組寫入從位置 offset 開始的可寫緩衝區 buffer。請注意,offset 是一個必需引數。
- struct.unpack(format, buffer)¶
從緩衝區 buffer(可能由
pack(format, ...)
打包)中根據格式字串 format 進行解包。結果是一個元組,即使它只包含一個專案。緩衝區的位元組數必須與格式所需的大小匹配,如calcsize()
所反映的。
- struct.unpack_from(format, buffer, offset=0)¶
從緩衝區 buffer 從位置 offset 開始,根據格式字串 format 進行解包。結果是一個元組,即使它只包含一個專案。緩衝區中的位元組數,從位置 offset 開始,必須至少等於格式所需的大小,如
calcsize()
所反映的。
- struct.iter_unpack(format, buffer)¶
從緩衝區 buffer 中根據格式字串 format 進行迭代解包。此函式返回一個迭代器,該迭代器將從緩衝區中讀取等大小的塊,直到消耗完其所有內容。緩衝區的位元組數必須是格式所需大小的倍數,如
calcsize()
所反映的。每次迭代都根據格式字串生成一個元組。
在 3.4 版本加入。
- struct.calcsize(format)¶
返回對應于格式字串 format 的結構(以及因此由
pack(format, ...)
生成的 bytes 物件)的大小。
格式字串¶
格式字串描述了打包和解包資料時的資料佈局。它們由 格式字元 構成,這些字元指定了正在打包/解包的資料型別。此外,特殊字元控制 位元組順序、大小和對齊。每個格式字串由一個可選的字首字元(描述資料的整體屬性)和一個或多個格式字元(描述實際資料值和填充)組成。
位元組順序、大小和對齊¶
預設情況下,C 型別以機器的原生格式和位元組順序表示,並透過跳過填充位元組(如果需要,根據 C 編譯器使用的規則)進行正確對齊。這種行為被選擇的目的是使打包的結構位元組與相應 C 結構的記憶體佈局完全匹配。是使用原生位元組順序和填充還是標準格式取決於應用程式。
或者,格式字串的第一個字元可以用於根據下表指示打包資料的位元組順序、大小和對齊方式:
字元 |
位元組順序 |
大小 |
對齊 |
---|---|---|---|
|
原生 |
原生 |
原生 |
|
原生 |
標準 |
無 |
|
小端 |
標準 |
無 |
|
大端 |
標準 |
無 |
|
網路 (= 大端) |
標準 |
無 |
如果第一個字元不是其中之一,則假定為 '@'
。
備註
數字 1023(十六進位制為 0x3ff
)具有以下位元組表示:
大端(
>
)的03 ff
小端(
<
)的ff 03
Python 示例
>>> import struct
>>> struct.pack('>h', 1023)
b'\x03\xff'
>>> struct.pack('<h', 1023)
b'\xff\x03'
原生位元組順序是大端或小端,具體取決於主機系統。例如,Intel x86、AMD64 (x86-64) 和 Apple M1 是小端;IBM z 和許多舊架構是大端。使用 sys.byteorder
來檢查系統的位元組序。
原生大小和對齊是使用 C 編譯器的 sizeof
表示式確定的。這始終與原生位元組順序結合使用。
標準大小僅取決於格式字元;請參閱 格式字元 部分的表格。
請注意 '@'
和 '='
之間的區別:兩者都使用原生位元組順序,但後者的尺寸和對齊是標準化的。
'!'
形式表示網路位元組順序,根據 IETF RFC 1700 定義,網路位元組順序始終是大端。
無法指示非原生位元組順序(強制位元組交換);請使用 '<'
或 '>'
的適當選擇。
備註
填充僅在連續的結構成員之間自動新增。在編碼結構的開頭或結尾不新增填充。
使用非原生大小和對齊(例如,使用‘<’、‘>’、‘=’和‘!’)時,不新增填充。
為了將結構末尾對齊到特定型別的對齊要求,請在格式字串末尾加上該型別的程式碼,重複次數為零。請參閱 示例。
格式字元¶
格式字元的含義如下;C 和 Python 值之間的轉換根據其型別應該是顯而易見的。‘標準大小’列是指使用標準大小時打包值的位元組大小;也就是說,當格式字串以 '<'
、'>'
、'!'
或 '='
開頭時。使用原生大小時,打包值的尺寸取決於平臺。
格式 |
C 型別 |
Python 型別 |
標準大小 |
備註 |
---|---|---|---|---|
|
填充位元組 |
無值 |
(7) |
|
|
char |
長度為 1 的位元組 |
1 |
|
|
signed char |
整數 |
1 |
(1), (2) |
|
unsigned char |
整數 |
1 |
(2) |
|
_Bool |
bool |
1 |
(1) |
|
short |
整數 |
2 |
(2) |
|
unsigned short |
整數 |
2 |
(2) |
|
int |
整數 |
4 |
(2) |
|
unsigned int |
整數 |
4 |
(2) |
|
long |
整數 |
4 |
(2) |
|
unsigned long |
整數 |
4 |
(2) |
|
long long |
整數 |
8 |
(2) |
|
unsigned long long |
整數 |
8 |
(2) |
|
|
整數 |
(3) |
|
|
|
整數 |
(3) |
|
|
(6) |
浮點數 |
2 |
(4) |
|
浮點數 |
浮點數 |
4 |
(4) |
|
double |
浮點數 |
8 |
(4) |
|
float complex |
complex |
8 |
(10) |
|
double complex |
complex |
16 |
(10) |
|
char[] |
bytes |
(9) |
|
|
char[] |
bytes |
(8) |
|
|
void* |
整數 |
(5) |
已於 3.3 版本更改: 增加了對 'n'
和 'N'
格式的支援。
已於 3.6 版本更改: 增加了對 'e'
格式的支援。
已於 3.14 版本更改: 增加了對 'F'
和 'D'
格式的支援。
備註
'?'
轉換碼對應於 C 標準自 C99 以來的 _Bool 型別。在標準模式下,它由一個位元組表示。當嘗試使用任何整數轉換碼打包非整數時,如果非整數具有
__index__()
方法,則會呼叫該方法將引數轉換為整數後再進行打包。已於 3.2 版本更改: 增加了對非整數使用
__index__()
方法的支援。'n'
和'N'
轉換碼僅適用於原生大小(透過預設選擇或使用'@'
位元組順序字元)。對於標準大小,可以使用適合您應用程式的其他整數格式。對於
'f'
、'd'
和'e'
轉換碼,打包的表示形式使用 IEEE 754 binary32、binary64 或 binary16 格式(分別對應於'f'
、'd'
或'e'
),而與平臺使用的浮點格式無關。'P'
格式字元僅適用於原生位元組順序(透過預設選擇或使用'@'
位元組順序字元)。位元組順序字元'='
根據主機系統選擇使用小端或大端位元組序。struct 模組不會將其解釋為原生順序,因此'P'
格式不可用。IEEE 754 binary16“半精度”型別是在 IEEE 754 標準 2008 年修訂版中引入的。它有一個符號位、一個 5 位指數和 11 位精度(顯式儲存 10 位),並且可以以全精度表示大約
6.1e-05
到6.5e+04
之間的數字。此型別不受 C 編譯器廣泛支援:在典型機器上,可以使用 unsigned short 進行儲存,但不能用於數學運算。有關更多資訊,請參閱維基百科上的 半精度浮點格式 頁面。打包時,
'x'
插入一個 NUL 位元組。'p'
格式字元編碼“Pascal 字串”,表示一個短的變長字串,儲存在固定數量的位元組中,該數量由計數指定。儲存的第一個位元組是字串的長度,或者 255,取兩者中較小者。字串的位元組跟在後面。如果傳遞給pack()
的字串太長(長於計數減 1),則只儲存字串的前count-1
個位元組。如果字串比count-1
短,則用空位元組填充,以確保總共使用 count 個位元組。請注意,對於unpack()
,'p'
格式字元消耗count
個位元組,但返回的字串永遠不會包含超過 255 個位元組。對於
's'
格式字元,計數被解釋為位元組的長度,而不是像其他格式字元那樣是重複計數;例如,'10s'
表示一個單個的 10 位元組字串,對映到一個 Python 位元組字串,而'10c'
表示 10 個單獨的單位元組字元元素(例如,cccccccccc
),對映到十個不同的 Python 位元組物件。(請參閱 示例 以獲得明確的差異演示。)如果未給出計數,則預設為 1。對於打包,字串將被截斷或用空位元組填充(根據需要),以使其適合。對於解包,結果的 bytes 物件始終具有指定的位元組數。作為特殊情況,'0s'
表示一個單獨的空字串(而'0c'
表示 0 個字元)。對於
'F'
和'D'
格式字元,打包的表示形式使用 IEEE 754 binary32 和 binary64 格式作為複數的組成部分,而與平臺使用的浮點格式無關。請注意,複數型別(F
和D
)是無條件可用的,儘管複數型別在 C 中是可選功能。如 C11 標準中所述,每個複數型別由一個兩元素的 C 陣列表示,分別包含實部和虛部。
格式字元前面可以有一個整數重複計數。例如,格式字串 '4h'
與 'hhhh'
的含義完全相同。
格式之間的空白字元被忽略;計數及其格式不得包含空格。
當使用整數格式('b'
、'B'
、'h'
、'H'
、'i'
、'I'
、'l'
、'L'
、'q'
、'Q'
)之一打包值 x
時,如果 x
超出了該格式的有效範圍,則會引發 struct.error
。
已於 3.1 版本更改: 以前,一些整數格式會包裝越界值,並引發 DeprecationWarning
而不是 struct.error
。
對於 '?'
格式字元,返回值是 True
或 False
。打包時,使用引數物件的布林值。原生或標準布林表示中的 0 或 1 將被打包,而任何非零值在解包時都將是 True
。
示例¶
備註
原生位元組順序示例(由 '@'
格式字首或缺少任何字首字元指定)可能與讀者機器的輸出不匹配,因為這取決於平臺和編譯器。
使用大端位元組序打包和解包三種不同大小的整數
>>> from struct import *
>>> pack(">bhl", 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('>bhl')
7
嘗試打包一個對於定義的欄位來說過大的整數
>>> pack(">h", 99999)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
struct.error: 'h' format requires -32768 <= number <= 32767
演示 's'
和 'c'
格式字元之間的區別
>>> pack("@ccc", b'1', b'2', b'3')
b'123'
>>> pack("@3s", b'123')
b'123'
可以透過將解包的欄位分配給變數或將結果包裝在命名元組中來命名
>>> record = b'raymond \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = unpack('<10sHHb', record)
>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8)
在原生模式下,格式字元的順序可能會影響大小,因為填充是隱式的。在標準模式下,使用者負責插入任何所需的填充。請注意,在下面的第一個 pack
呼叫中,在打包的 '#'
之後添加了三個 NUL 位元組,以便將後續的整數對齊到四位元組邊界。在此示例中,輸出是在小端機器上生成的
>>> pack('@ci', b'#', 0x12131415)
b'#\x00\x00\x00\x15\x14\x13\x12'
>>> pack('@ic', 0x12131415, b'#')
b'\x15\x14\x13\x12#'
>>> calcsize('@ci')
8
>>> calcsize('@ic')
5
假定平臺的長整型對齊在 4 位元組邊界上,下面的格式 'llh0l'
在末尾添加了兩個填充位元組
>>> pack('@llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'
應用¶
struct
模組存在兩種主要應用:應用程式內 Python 程式碼和 C 程式碼之間的資料交換(原生格式),以及使用約定好的資料佈局的應用程式之間的資料交換(標準格式)。總的來說,為這兩個領域構建的格式字串是不同的。
原生格式¶
在構造模仿原生布局的格式字串時,編譯器和機器架構決定了位元組順序和填充。在這種情況下,應使用 @
格式字元來指定原生位元組順序和資料大小。內部填充位元組通常會自動插入。可能需要在格式字串的末尾使用零重複格式程式碼,以向上舍入到正確的位元組邊界,從而正確對齊連續的資料塊。
考慮以下兩個簡單的示例(在 64 位小端機器上)
>>> calcsize('@lhl')
24
>>> calcsize('@llh')
18
在沒有額外填充的情況下,資料在第二個格式字串的末尾不會填充到 8 位元組邊界。零重複格式程式碼可以解決這個問題
>>> calcsize('@llh0l')
24
'x'
格式程式碼可用於指定重複,但對於原生格式,最好使用零重複格式,如 '0l'
。
預設情況下,使用原生位元組順序和對齊,但最好明確指定並使用 '@'
字首字元。
標準格式¶
在交換程序外資料(例如網路或儲存)時,請務必精確。指定確切的位元組順序、大小和對齊。不要假設它們與特定機器的原生順序匹配。例如,網路位元組順序是大端,而許多流行的 CPU 是小端。透過明確定義這一點,使用者無需關心其程式碼執行的平臺的具體細節。第一個字元通常應該是 <
或 >
(或 !
)。填充是程式設計師的責任。零重複格式字元將不起作用。相反,使用者必須在需要的地方顯式新增 'x'
填充位元組。回顧上一節的示例,我們有
>>> calcsize('<qh6xq')
24
>>> pack('<qh6xq', 1, 2, 3) == pack('@lhl', 1, 2, 3)
True
>>> calcsize('@llh')
18
>>> pack('@llh', 1, 2, 3) == pack('<qqh', 1, 2, 3)
True
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
24
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
True
以上結果(在 64 位機器上執行)在不同機器上執行時可能不匹配。例如,以下示例在 32 位機器上執行
>>> calcsize('<qqh6x')
24
>>> calcsize('@llh0l')
12
>>> pack('@llh0l', 1, 2, 3) == pack('<qqh6x', 1, 2, 3)
False
類¶
struct
模組還定義了以下型別
- class struct.Struct(format)¶
返回一個新的 Struct 物件,該物件根據格式字串 format 讀寫二進位制資料。一次建立
Struct
物件並呼叫其方法比使用相同格式呼叫模組級函式更有效,因為格式字串只編譯一次。備註
傳遞給模組級函式的最新格式字串的已編譯版本會快取起來,因此只使用少量格式字串的程式無需擔心重用單個
Struct
例項。已編譯的 Struct 物件支援以下方法和屬性
- pack_into(buffer, offset, v1, v2, ...)¶
與
pack_into()
函式相同,使用已編譯的格式。
- unpack_from(buffer, offset=0)¶
與
unpack_from()
函式相同,使用已編譯的格式。緩衝區中的位元組數,從位置 offset 開始,必須至少等於size
。
- iter_unpack(buffer)¶
與
iter_unpack()
函式相同,使用已編譯的格式。緩衝區中的位元組數必須是size
的倍數。在 3.4 版本加入。
- format¶
用於構造此 Struct 物件的格式字串。
已於 3.13 版本更改: Struct 的 repr() 已更改。現在是
>>> Struct('i') Struct('i')