7.1. struct — 将字节解释为打包的二进制数据 — Python 文档
7.1. 结构 — 将字节解释为打包的二进制数据
该模块在 Python 值和表示为 Python bytes 对象的 C 结构之间执行转换。 这可用于处理存储在文件中或来自网络连接等来源的二进制数据。 它使用 格式字符串 作为 C 结构布局的紧凑描述以及与 Python 值之间的预期转换。
笔记
默认情况下,打包给定 C 结构的结果包括填充字节,以便为所涉及的 C 类型保持正确对齐; 同样,拆包时也要考虑对齐。 选择此行为是为了使打包结构的字节与相应 C 结构在内存中的布局完全对应。 要处理与平台无关的数据格式或省略隐式填充字节,请使用 standard
大小和对齐而不是 native
大小和对齐:请参阅 字节顺序、大小和对齐 细节。
几个 struct 函数(以及 Struct 的方法)采用 buffer 参数。 这是指实现 缓冲区协议 并提供可读或可读写缓冲区的对象。 用于该目的的最常见类型是 bytes 和 bytearray,但许多其他类型可以被视为字节数组实现了缓冲协议,以便它们可以被读取/填充而无需从 bytes 对象进行额外复制。
7.1.1. 函数和异常
该模块定义了以下异常和函数:
- exception struct.error
- 在各种场合提出的异常; 参数是一个描述错误的字符串。
- struct.pack(fmt, v1, v2, ...)
- 返回一个包含值 v1, v2, ... 根据格式字符串 fmt 打包的字节对象。 参数必须与格式所需的值完全匹配。
- struct.pack_into(fmt, buffer, offset, v1, v2, ...)
- 根据格式字符串 fmt 将值 v1, v2, ... 打包并写入可写缓冲区 buffer 开始于位置 偏移量 。 请注意, offset 是必需的参数。
- struct.unpack(fmt, buffer)
- 根据格式字符串 fmt 从缓冲区 buffer(大概是由
pack(fmt, ...)
打包)解包。 结果是一个元组,即使它只包含一个项目。 缓冲区的字节大小必须与格式要求的大小相匹配,如 calcsize() 所反映的那样。
- struct.unpack_from(fmt, buffer, offset=0)
- 根据格式字符串 fmt 从位置 offset 开始从 buffer 解包。 结果是一个元组,即使它只包含一个项目。 缓冲区的字节大小减去 offset,必须至少是格式所需的大小,如 calcsize() 所反映的那样。
- struct.iter_unpack(fmt, buffer)
根据格式字符串 fmt 从缓冲区 buffer 中迭代解包。 此函数返回一个迭代器,它将从缓冲区读取相同大小的块,直到其所有内容都已被消耗。 缓冲区的字节大小必须是格式所需大小的倍数,如 calcsize() 所反映的那样。
每次迭代都会产生一个由格式字符串指定的元组。
3.4 版中的新功能。
- struct.calcsize(fmt)
- 返回与格式字符串 fmt 对应的结构的大小(以及由
pack(fmt, ...)
生成的字节对象)的大小。
7.1.2. 格式化字符串
格式字符串是用于在打包和解包数据时指定预期布局的机制。 它们由 格式字符 构成,它指定被打包/解包的数据类型。 此外,还有用于控制 字节顺序、大小和对齐 的特殊字符。
7.1.2.1. 字节顺序、大小和对齐
默认情况下,C 类型以机器的本机格式和字节顺序表示,并在必要时通过跳过填充字节来正确对齐(根据 C 编译器使用的规则)。
或者,可以使用格式字符串的第一个字符来指示打包数据的字节顺序、大小和对齐方式,如下表所示:
特点 | 字节顺序 | 尺寸 | 结盟 |
---|---|---|---|
@
|
本国的 | 本国的 | 本国的 |
=
|
本国的 | 标准 | 没有任何 |
<
|
小端 | 标准 | 没有任何 |
>
|
大端 | 标准 | 没有任何 |
!
|
网络(=大端) | 标准 | 没有任何 |
如果第一个字符不是这些字符中的一个,则假定为 '@'
。
本机字节顺序是大端或小端,具体取决于主机系统。 例如,Intel x86 和 AMD64 (x86-64) 是小端; 摩托罗拉 68000 和 PowerPC G5 是大端; ARM 和 Intel Itanium 具有可切换的字节序(双字节序)。 使用 sys.byteorder
检查系统的字节序。
本机大小和对齐方式是使用 C 编译器的 sizeof
表达式确定的。 这总是与本机字节顺序相结合。
标准大小仅取决于格式字符; 请参阅 格式字符 部分中的表格。
注意 '@'
和 '='
的区别:两者都使用原生字节顺序,但后者的大小和对齐方式是标准化的。
形式 '!'
可用于那些声称无法记住网络字节顺序是大端还是小端的可怜人。
无法指示非本机字节顺序(强制字节交换); use the appropriate choice of '<'
or '>'
.
笔记:
- 填充仅在连续的结构成员之间自动添加。 在编码结构的开头或结尾不添加填充。
- 使用非原生大小和对齐时不添加填充,例如 带有“”、“=”和“!”。
- 要将结构的结尾与特定类型的对齐要求对齐,请使用重复计数为零的该类型的代码结束格式。 请参阅 示例 。
7.1.2.2. 格式字符
格式字符有以下含义; 考虑到它们的类型,C 和 Python 值之间的转换应该是显而易见的。 'Standard size'列是指使用标准大小时打包值的大小(以字节为单位); 即,当格式字符串以 '<'
、'>'
、'!'
或 '='
之一开头时。 使用本机大小时,打包值的大小取决于平台。
格式 | C型 | 蟒蛇型 | 标准尺寸 | 笔记 |
---|---|---|---|---|
x
|
填充字节 | 没有价值 | ||
c
|
char
|
长度为 1 的字节 | 1 | |
b
|
signed char
|
整数 | 1 | (1),(3) |
B
|
unsigned char
|
整数 | 1 | (3) |
?
|
_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'
格式的支持。
笔记:
'?'
转换码对应C99定义的_Bool
类型。 如果此类型不可用,则使用char
进行模拟。 在标准模式下,它总是用一个字节表示。'q'
和'Q'
转换代码仅在平台 C 编译器支持 Clong long
或在 Windows 上支持__int64
时才在本机模式下可用。 它们在标准模式下始终可用。当尝试使用任何整数转换代码打包非整数时,如果非整数具有
__index__()
方法,则在打包之前调用该方法将参数转换为整数。3.2 版更改: 对非整数使用
__index__()
方法是 3.2 中的新功能。'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 编译器并不广泛支持这种类型:在典型机器上,无符号短整型可用于存储,但不能用于数学运算。 有关更多信息,请参阅 半精度浮点格式 上的维基百科页面。
格式字符前面可能有一个整数重复计数。 例如,格式字符串 '4h'
的含义与 'hhhh'
完全相同。
格式之间的空白字符被忽略; 但是,计数及其格式不得包含空格。
对于 's'
格式字符,计数被解释为字节的长度,而不是像其他格式字符那样重复计数; 例如,'10s'
表示单个 10 字节字符串,而 '10c'
表示 10 个字符。 如果未给出计数,则默认为 1。 对于打包,字符串被截断或适当地用空字节填充以使其适合。 对于解包,生成的字节对象始终具有指定的字节数。 作为特殊情况,'0s'
表示单个空字符串(而 '0c'
表示 0 个字符)。
使用整数格式之一打包值 x
时 ('b'
、'B'
、'h'
、'H'
、'i'
]、'I'
、'l'
、'L'
、'q'
、'Q'
),如果x
在有效范围之外对于该格式,则会引发 struct.error。
3.1 版更改: 在 3.0 中,一些整数格式包装了超出范围的值并引发 DeprecationWarning 而不是 struct.error。
'p'
格式字符编码“Pascal 字符串”,意思是存储在 固定字节数 中的短可变长度字符串,由计数给出。 存储的第一个字节是字符串的长度,或 255,以较小者为准。 字符串的字节如下。 如果传入 pack() 的字符串太长(比计数减 1 长),则只存储字符串的前导 count-1
字节。 如果字符串比 count-1
短,则用空字节填充它,以便完全使用 count 个字节。 请注意,对于 unpack(),'p'
格式字符消耗 count
字节,但返回的字符串不能包含超过 255 个字节。
对于 '?'
格式字符,返回值为 True 或 False。 打包时,使用参数对象的真值。 本机或标准 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'
这仅在本机大小和对齐有效时才有效; 标准尺寸和对齐不强制任何对齐。
7.1.3. 班级
struct 模块还定义了以下类型:
- class struct.Struct(format)
返回一个新的 Struct 对象,它根据格式字符串 格式 写入和读取二进制数据。 创建一次 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 对象的格式字符串。