struct --- 将字节串解读为打包的二进制数据

源代码: Lib/struct.py


此模块可在 Python 值和以 Python bytes 对象表示的 C 结构体之间进行转换。通过紧凑 格式字符串 描述预期的 Python 值转换目标/来源。 此模块的函数和对象可被用于两种相当不同的应用程序,与外部源(文件或网络连接)进行数据交换,或者在 Python 应用和 C 层级之间进行数据传输。

备注

当未给出前缀字符时,将默认为原生模式。它会基于构建 Python 解释器的平台和编译器来打包和解包数据。打包一个给定 C 结构体的结果包括为所涉及的 C 类型保持正确对齐的填充字节;类似地,当解包时也会将对齐纳入考虑。 相反地,当在外部源之间进行数据通信时,将由程序员负责定义字节顺序和元素之间的填充。请参阅 字节顺序,大小和对齐方式 了解详情。

某些 struct 函数(和 Struct 的方法)接受一个 buffer 参数。这是指实现了 缓冲协议 并提供可读或可读写缓冲区的对象。用于此目的的最常见类型是 bytesbytearray,但许多其他可被视为字节数组的类型也实现了缓冲区协议,因此它们可以在无需从 bytes 对象进行额外复制的情况下被读取/填充。

函数和异常

此模块定义了下列异常和函数:

exception struct.error

会在多种场合下被引发的异常;其参数为一个描述错误信息的字符串。

struct.pack(format, v1, v2, ...)

返回一个 bytes 对象,其中包含根据格式字符串 format 打包的值 v1, v2, ... 参数个数必须与格式字符串所要求的值完全匹配。

struct.pack_into(format, buffer, offset, v1, v2, ...)

根据格式字符串 format 打包 v1, v2, ... 等值并将打包的字节串写入可写缓冲区 bufferoffset 开始的位置。请注意 offset 是必需的参数。

struct.unpack(format, buffer)

根据格式字符串 format 从缓冲区 buffer 解包(假定是由 pack(format, ...) 打包)。 结果为一个元组,即使其只包含一个条目。缓冲区的字节大小必须匹配格式所要求的大小,如 calcsize() 所示。

struct.unpack_from(format, /, buffer, offset=0)

buffer 从位置 offset 开始根据格式字符串 format 进行解包。结果为一个元组,即使其中只包含一个条目。 缓冲区的字节大小从位置 offset 开始必须至少为 calcsize() 显示的格式所要求的大小。

struct.iter_unpack(format, buffer)

根据格式字符串 format 以迭代方式从缓冲区 buffer 中解包。 此函数返回一个迭代器,它将从缓冲区读取大小相等的块直到其所有内容耗尽为止。缓冲区的字节大小必须是格式所要求的大小的整数倍,如 calcsize() 所显示的。

每次迭代将产生一个如格式字符串所指定的元组。

Added in version 3.4.

struct.calcsize(format)

返回与格式字符串 format 相对应的结构的大小(亦即 pack(format, ...) 所产生的字节串对象的大小)。

格式字符串

Format strings describe the data layout when packing and unpacking data. They are built up from type codes, which specify the type of data being packed/unpacked. In addition, special characters control the byte order, size and alignment. Each format string consists of an optional prefix character which describes the overall properties of the data and one or more format characters which describe the actual data values and padding.

字节顺序,大小和对齐方式

在默认情况下,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 表达式来确定的。这总是会与本机字节顺序相绑定。

Standard size depends only on the type code; see the table in the Type Codes section.

请注意 '@''=' 之间的区别:两个都使用本机字节顺序,但后者的大小和对齐方式是标准化的。

形式 '!' 代表网络字节顺序总是使用在 IETF RFC 1700 中所定义的大端序。

没有什么方式能指定非本机字节顺序(强制字节对调);请正确选择使用 '<''>'

注意:

  1. 填充只会在连续结构成员之间自动添加。填充不会添加到已编码结构的开头和末尾。

  2. 当使用非本机大小和对齐方式即 '<', '>', '=', and '!' 时不会添加任何填充。

  3. 要将结构的末尾对齐到符合特定类型的对齐要求,请以该类型代码加重复计数的零作为格式结束。参见 例子

Type Codes

Type codes (or format codes) have the following meaning; the conversion between C and Python values should be obvious given their types. The 'Standard size' column refers to the size of the packed value in bytes when using standard size; that is, when the format string starts with one of '<', '>', '!' or '='. When using native size, the size of the packed value is platform-dependent.

格式

C 类型

Python 类型

标准大小

备注

x

填充字节

(7)

c

char

长度为 1 的字节串

1

b

signed char

int

1

(2)

B

unsigned char

int

1

(2)

?

_Bool

bool

1

(1)

h

short

int

2

(2)

H

unsigned short

int

2

(2)

i

int

int

4

(2)

I

unsigned int

int

4

(2)

l

long

int

4

(2)

L

unsigned long

int

4

(2)

q

long long

int

8

(2)

Q

unsigned long long

int

8

(2)

n

ssize_t

int

(2), (3)

N

size_t

int

(2), (3)

e

_Float16

float

2

(4), (6)

f

float

float

4

(4)

d

double

float

8

(4)

F

float complex

复数

8

(10)

D

double complex

复数

16

(10)

Zf

float complex

复数

8

(10)

Zd

double complex

复数

16

(10)

s

char[]

字节串

(9)

p

char[]

字节串

(8)

P

void*

int

(2), (5)

在 3.3 版本发生变更: 增加了对 'n''N' 格式的支持

在 3.6 版本发生变更: 添加了对 'e' 格式的支持。

在 3.14 版本发生变更: 增加了对 'F''D' 格式的支持。

在 3.15 版本发生变更: Added support for the 'Zf' and 'Zd' formats.

参见

arrayctypes 模块,以及某些第三方模块如 numpy 均使用类似 -- 但略有差异的 -- 类型代码。

注意:

  1. '?' 转换码对应于自 C99 开始由 C 标准所定义的 _Bool 类型。在标准模式下,它总是以一个字节来表示。

  2. 当尝试使用任何整数转换码打包一个非整数时,如果该非整数具有 __index__() 方法,则会在打包之前将参数转换为一个整数。

    在 3.2 版本发生变更: 增加了用于非整数的 __index__() 方法。

  3. 'n''N' 转换码仅对本机大小可用(选择为默认或使用 '@' 字节顺序字符)。 对于标准大小,你可以使用适合你的应用的任何其他整数格式。

  4. 对于 'f', 'd''e' 转换码,打包表示形式将使用 IEEE 754 binary32, binary64 或 binary16 格式 (分别对应于 'f', 'd''e'),无论平台使用何种浮点格式。

  5. The 'P' type code is only available for the native byte ordering (selected as the default or with the '@' byte order character). The byte order character '=' chooses to use little- or big-endian ordering based on the host system. The struct module does not interpret this as native ordering, so the 'P' format is not available.

  6. IEEE 754 binary16 "半精度" 类型是在 IEEE 754 标准 的 2008 修订版中引入的。 它包含一个符号位,5 个指数位和 11 个精度位(明确存储 10 位),可用完全精度表示大致范围在 6.1e-056.5e+04 之间的数字。 此类型并不被 C 编译器广泛支持:它可以作为 _Float16 类型,如果编译器支持 C23 标准附件 H 的话。 在一台典型的机器上,可以使用 unsigned short 来存储,但不会被用于数学运算。 请参阅 Wikipedia 页面 half-precision floating-point format 了解详情。

  7. 在打包时,'x' 会插入一个 NUL 字节。

  8. The 'p' type code encodes a "Pascal string", meaning a short variable-length string stored in a fixed number of bytes, given by the count. The first byte stored is the length of the string, or 255, whichever is smaller. The bytes of the string follow. If the byte string passed in to pack() is too long (longer than the count minus 1), only the leading count-1 bytes of the string are stored. If the byte string is shorter than count-1, it is padded with null bytes so that exactly count bytes in all are used. Note that for unpack(), the 'p' type code consumes count bytes, but that the bytes object returned can never contain more than 255 bytes. When packing, arguments of types bytes and bytearray are accepted.

  9. For the 's' type code, the count is interpreted as the length of the byte string, not a repeat count like for the other type codes; for example, '10s' means a single 10-byte string mapping to or from a single Python byte string, while '10c' means 10 separate one byte character elements (e.g., cccccccccc) mapping to or from ten different Python byte objects. (See 例子 for a concrete demonstration of the difference.) If a count is not given, it defaults to 1. For packing, the byte string is truncated or padded with null bytes as appropriate to make it fit. For unpacking, the resulting bytes object always has exactly the specified number of bytes. As a special case, '0s' means a single, empty byte string (while '0c' means 0 characters). When packing, arguments of types bytes and bytearray are accepted.

  10. For the 'F' and 'D' type codes, the packed representation uses the IEEE 754 binary32 and binary64 format for components of the complex number, regardless of the floating-point format used by the platform. Note that complex types (F/Zf and D/Zd) are available unconditionally, despite complex types being an optional feature in C. As specified in the C11 standard, each complex type is represented by a two-element C array containing, respectively, the real and imaginary parts.

A type code may be preceded by an integral repeat count. For example, the format string '4h' means exactly the same as 'hhhh'.

格式之间的空白字符会被忽略;但是计数及其格式字符中不可有空白字符。

当使用某一种整数格式 ('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q') 打包值 x 时,如果 x 在该格式的有效范围之外则将引发 struct.error.

在 3.1 版本发生变更: 在之前版本中,某些整数格式包装了超范围的值并会引发 DeprecationWarning 而不是 struct.error

For the '?' type code, the return value is either True or False. When packing, the truth value of the argument object is used. Either 0 or 1 in the native or standard bool representation will be packed, and any non-zero value will be True when unpacking.

例子

备注

原生字节顺序的示例 (由 '@' 格式前缀或不带任何前缀字符的形式指定) 可能与读者机器所产生的内容不匹配,因为这取决于具体的平台和编译器。

打包和解包三种不同大小的整数,使用大端序:

>>> 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)

The ordering of type codes may have an impact on size in native mode since padding is implicit. In standard mode, the user is responsible for inserting any desired padding. Note in the first pack call below that three NUL bytes were added after the packed '#' to align the following integer on a four-byte boundary. In this example, the output was produced on a little endian machine:

>>> 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

以下格式 'llh0l' 将会在末尾添加两个填充字节,假定平台的 long 类型按 4 个字节的边界对齐的话:

>>> pack('@llh0l', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'

参见

模块 array

被打包为二进制存储的同质数据。

模块 json

JSON 编码器和解码器。

模块 pickle

Python 对象序列化。

应用

struct 模块有两种主要用途,在应用程序内部 Python 与 C 代码之间或与使用同一编译器编译的另一个应用程序之间的数据交换 (原生格式),以及使用商定的数据布局在应用程序之间的数据交换 (标准格式)。一般来说,为这两个领域构造的格式字符串是不同的。

原生格式

When constructing format strings which mimic native layouts, the compiler and machine architecture determine byte ordering and padding. In such cases, the @ format character should be used to specify native byte ordering and data sizes. Internal pad bytes are normally inserted automatically. It is possible that a zero-repeat type code will be needed at the end of a format string to round up to the correct byte boundary for proper alignment of consecutive chunks of data.

请看这两个简单的示例(在 64 位的小端序机器上):

>>> calcsize('@lhl')
24
>>> calcsize('@llh')
18

在不使用额外填充的情况下不会将数据填充到第二个格式字符串末尾的 8 字节边界上。零重复的格式代码解决了这个问题:

>>> calcsize('@llh0l')
24

The 'x' type code can be used to specify the repeat, but for native formats it is better to use a zero-repeat format like '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(v1, v2, ...)

等价于 pack() 函数,使用了已编译的格式。 (len(result) 将等于 size。)

pack_into(buffer, offset, v1, v2, ...)

等价于 pack_into() 函数,使用了已编译的格式。

unpack(buffer)

等价于 unpack() 函数,使用了已编译的格式。缓冲区的字节大小必须等于 size

unpack_from(buffer, offset=0)

等价于 unpack_from() 函数,使用了已编译的格式。缓冲区的字节大小从位置 offset 开始必须至少为 size.

iter_unpack(buffer)

等价于 iter_unpack() 函数,使用了已编译的格式。缓冲区的大小必须为 size 的整数倍。

Added in version 3.4.

format

用于构造此 Struct 对象的格式字符串。

在 3.7 版本发生变更: 格式字符串类型现在是 str 而不再是 bytes

size

计算出对应于 format 的结构大小(亦即 pack() 方法所产生的字节串对象的大小)。

在 3.13 版本发生变更: 结构体的 repr() 已被修改。现在将为:

>>> Struct('i')
Struct('i')