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

源代码: Lib/struct.py


此模块可以执行 Python 值和以 Python bytes 对象表示的 C 结构之间的转换。 这可以被用来处理存储在文件中或是从网络连接等其他来源获取的二进制数据。 它使用 格式字符串 作为 C 结构布局的精简描述以及与 Python 值的双向转换。

注解

默认情况下,打包给定 C 结构的结果会包含填充字节以使得所涉及的 C 类型保持正确的对齐;类似地,对齐在解包时也会被纳入考虑。 选择此种行为的目的是使得被打包结构的字节能与相应 C 结构在内存中的布局完全一致。 要处理平台独立的数据格式或省略隐式的填充字节,请使用 standard 大小和对齐而不是 native 大小和对齐:详情参见 字节顺序,大小和对齐方式

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

7.1.1. 函数和异常

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

exception struct.error

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

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

Return a bytes object containing the values v1, v2, … packed according to the format string fmt. The arguments must match the values required by the format exactly.

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

Pack the values v1, v2, … according to the format string fmt and write the packed bytes into the writable buffer buffer starting at position offset. Note that offset is a required argument.

struct.unpack(fmt, buffer)

Unpack from the buffer buffer (presumably packed by pack(fmt, ...)) according to the format string fmt. The result is a tuple even if it contains exactly one item. The buffer’s size in bytes must match the size required by the format, as reflected by calcsize().

struct.unpack_from(fmt, buffer, offset=0)

Unpack from buffer starting at position offset, according to the format string fmt. The result is a tuple even if it contains exactly one item. The buffer’s size in bytes, minus offset, must be at least the size required by the format, as reflected by calcsize().

struct.iter_unpack(fmt, buffer)

Iteratively unpack from the buffer buffer according to the format string fmt. This function returns an iterator which will read equally-sized chunks from the buffer until all its contents have been consumed. The buffer’s size in bytes must be a multiple of the size required by the format, as reflected by calcsize().

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

3.4 新版功能.

struct.calcsize(fmt)

Return the size of the struct (and hence of the bytes object produced by pack(fmt, ...)) corresponding to the format string fmt.

7.1.2. 格式字符串

格式字符串是用来在打包和解包数据时指定预期布局的机制。 它们使用指定被打包/解包数据类型的 格式字符 进行构建。 此外,还有一些特殊字符用来控制 字节顺序,大小和对齐方式

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

默认情况下,C类型以机器的本机格式和字节顺序表示,并在必要时通过跳过填充字节进行正确对齐(根据C编译器使用的规则)。

或者,根据下表,格式字符串的第一个字符可用于指示打包数据的字节顺序,大小和对齐方式:

字符

字节顺序

大小

对齐方式

@

按原字节

按原字节

按原字节

=

按原字节

标准

<

小端

标准

>

大端

标准

!

网络(=大端)

标准

如果第一个字符不是其中之一,则假定为 '@'

本机字节顺序可能为大端或是小端,取决于主机系统的不同。 例如, Intel x86 和 AMD64 (x86-64) 是小端的;Motorola 68000 和 PowerPC G5 是大端的;ARM 和 Intel Itanium 具有可切换的字节顺序(双端)。 请使用 sys.byteorder 来检查你的系统字节顺序。

本机大小和对齐方式是使用 C 编译器的 sizeof 表达式来确定的。 这总是会与本机字节顺序相绑定。

标准大小仅取决于格式字符;请参阅 格式字符 部分中的表格。

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

格式 '!' 适合给那些宣称他们记不得网络字节顺序是大端还是小端的可怜人使用。

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

注释:

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

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

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

7.1.2.2. 格式字符

格式字符具有以下含义;C 和 Python 值之间的按其指定类型的转换应当是相当明显的。 ‘标准大小’列是指当使用标准大小时以字节表示的已打包值大小;也就是当格式字符串以 '<', '>', '!''=' 之一开头的情况。 当使用本机大小时,已打包值的大小取决于具体的平台。

格式

C 类型

Python 类型

标准大小

注释

x

填充字节

c

char

长度为 1 的字节串

1

b

signed char

整数

1

(1),(3)

B

unsigned char

整数

1

(3)

?

_Bool

bool

1

(1)

h

short

整数

2

(3)

H

unsigned short

整数

2

(3)

i

int

整数

4

(3)

I

unsigned int

整数

4

(3)

l

long

整数

4

(3)

L

unsigned long

整数

4

(3)

q

long long

整数

8

(2), (3)

Q

unsigned long long

整数

8

(2), (3)

n

ssize_t

整数

(4)

N

size_t

整数

(4)

e

(7)

浮点数

2

(5)

f

float

浮点数

4

(5)

d

double

浮点数

8

(5)

s

char[]

字节串

p

char[]

字节串

P

void *

整数

(6)

在 3.3 版更改: 增加了对 'n''N' 格式的支持

在 3.6 版更改: 添加了对 'e' 格式的支持。

注释:

  1. '?' 转换码对应于 C99 定义的 _Bool 类型。 如果此类型不可用,则使用 char 来模拟。 在标准模式下,它总是以一个字节表示。

  2. The 'q' and 'Q' conversion codes are available in native mode only if the platform C compiler supports C long long, or, on Windows, __int64. They are always available in standard modes.

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

    在 3.2 版更改: 为非整数使用 __index__() 方法是 3.2 版的新增特性。

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

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

  6. 'P' 格式字符仅对本机字节顺序可用(选择为默认或使用 '@' 字节顺序字符)。 字节顺序字符 '=' 选择使用基于主机系统的小端或大端排序。 struct 模块不会将其解读为本机排序,因此 'P' 格式将不可用。

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

格式字符之前可以带有整数重复计数。 例如,格式字符串 '4h' 的含义与 'hhhh' 完全相同。

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

对于 's' 格式字符,计数会被解析为字节的长度,而不是像其他格式字符那样的重复计数;例如,'10s' 表示一个 10 字节的字节串,而 '10c' 表示 10 个字符。 如果未给出计数,则默认值为 1。 对于打包操作,字节串会被适当地截断或填充空字节以符合要求。 对于解包操作,结果字节对象总是恰好具有指定数量的字节。 作为特殊情况,'0s' 表示一个空字符串(而 '0c' 表示 0 个字符)。

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

在 3.1 版更改: 在 3.0 中,某些包装了超范围值的整数格式会引发 DeprecationWarning 而不是 struct.error

'p' 格式字符用于编码“Pascal 字符串”,即存储在由计数指定的 固定长度字节 中的可变长度短字符串。 所存储的第一个字节为字符串长度或 255 中的较小值。 之后是字符串对应的字节。 如果传入 pack() 的字符串过长(超过计数值减 1),则只有字符串前 count-1 个字节会被存储。 如果字符串短于 count-1,则会填充空字节以使得恰好使用了 count 个字节。 请注意对于 unpack()'p' 格式字符会消耗 count 个字节,但返回的字符串永远不会包含超过 255 个字节。

对于 '?' 格式字符,返回值为 TrueFalse。 在打包时将会使用参数对象的逻辑值。 以本机或标准 bool 类型表示的 0 或 1 将被打包,任何非零值在解包时将为 True

7.1.2.3. 例子

注解

所有示例都假定使用一台大端机器的本机字节顺序、大小和对齐方式。

打包/解包三个整数的基础示例:

>>> from struct import *
>>> pack('hhl', 1, 2, 3)
b'\x00\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)
>>> calcsize('hhl')
8

解包的字段可通过将它们赋值给变量或将结果包装为一个具名元组来命名:

>>> 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('ci', b'*', 0x12131415)
b'*\x00\x00\x00\x12\x13\x14\x15'
>>> pack('ic', 0x12131415, b'*')
b'\x12\x13\x14\x15*'
>>> 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

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

模块 xdrlib

打包和解包 XDR 数据。

7.1.3.

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 的整数倍。

3.4 新版功能.

format

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

size

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