python3之struct包
1. 综述
此模块可以执行 Python 值和以 Python bytes 对象表示的 C 结构之间的转换。 这可以被用来处理存储在文件中或是从网络连接等其他来源获取的二进制数据。 它使用 格式字符串 作为 C 结构布局的精简描述以及与 Python 值的双向转换。
注解: 默认情况下,打包给定 C 结构的结果会包含填充字节以使得所涉及的 C 类型保持正确的对齐;类似地,对齐在解包时也会被纳入考虑。 选择此种行为的目的是使得被打包结构的字节能与相应 C 结构在内存中的布局完全一致。 要处理平台独立的数据格式或省略隐式的填充字节,请使用 standard 大小和对齐而不是 native 大小和对齐:详情参见 字节顺序,大小和对齐方式。
某些 struct 的函数(以及 Struct 的方法)接受一个 buffer 参数。 这将指向实现了 缓冲协议 并提供只读或是可读写缓冲的对象。 用于此目的的最常见类型为 bytes 和 bytearray,但许多其他可被视为字节数组的类型也实现了缓冲协议,因此它们无需额外从 bytes 对象复制即可被读取或填充。
2. 函数和异常
函数 | 说明 | 备注 |
---|---|---|
exception struct.error | 会在多种场合下被引发的异常;其参数为一个描述错误信息的字符串。 | |
struct.pack(format, v1, v2, …) | 返回一个 bytes 对象,其中包含根据格式字符串 format 打包的值 v1, v2, … 参数个数必须与格式字符串所要求的值完全匹配。 | |
struct.pack_into(format, buffer, offset, v1, v2, …) | 根据格式字符串 format 打包 v1, v2, … 等值并将打包的字节串写入可写缓冲区 buffer 从 offset 开始的位置。 请注意 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() 所示。 | 每次迭代将产生一个如格式字符串所指定的元组。 |
struct.calcsize(format) | 返回与格式字符串 format 相对应的结构的大小(亦即 pack(format, …) 所产生的字节串对象的大小)。 |
3. 字节顺序,大小和对齐方式
默认情况下,C类型以机器的本机格式和字节顺序表示,并在必要时通过跳过填充字节进行正确对齐(根据C编译器使用的规则)。或者,根据下表,格式字符串的第一个字符可用于指示打包数据的字节顺序,大小和对齐方式:
字符 | 字节顺序 | 大小 | 对齐方式 |
---|---|---|---|
@ | 按原字节 | 按原字节 | 按原字节 |
= | 按原字节 | 标准 | 无 |
< | 小端 | 标准 | 无 |
> | 大端 | 标准 | 无 |
! | 网络(=大端)标准 | 无 |
如果第一个字符不是其中之一,则假定为 ‘@’ 。
本机字节顺序可能为大端或是小端,取决于主机系统的不同。 例如, Intel x86 和 AMD64 (x86-64) 是小端的;Motorola 68000 和 PowerPC G5 是大端的;ARM 和 Intel Itanium 具有可切换的字节顺序(双端)。 请使用 sys.byteorder 来检查你的系统字节顺序。
本机大小和对齐方式是使用 C 编译器的 sizeof 表达式来确定的。 这总是会与本机字节顺序相绑定。
标准大小仅取决于格式字符;请参阅 格式字符 部分中的表格。
请注意 ‘@’ 和 ‘=’ 之间的区别:两个都使用本机字节顺序,但后者的大小和对齐方式是标准化的。
格式 ‘!’ 适合给那些宣称他们记不得网络字节顺序是大端还是小端的可怜人使用。
没有什么方式能指定非本机字节顺序(强制字节对调);请正确选择使用 ‘<’ 或 ‘>’。
注释:
- 填充只会在连续结构成员之间自动添加。 填充不会添加到已编码结构的开头和末尾。
- 当使用非本机大小和对齐方式即 ‘<’, ‘>’, ‘=’, and ‘!’ 时不会添加任何填充。
- 要将结构的末尾对齐到符合特定类型的对齐要求,请以该类型代码加重复计数的零作为格式结束。 参见 示例。
4. 格式字符
格式字符具有以下含义;C 和 Python 值之间的按其指定类型的转换应当是相当明显的。 ‘标准大小’列是指当使用标准大小时以字节表示的已打包值大小;也就是当格式字符串以 ‘<’, ‘>’, ‘!’ 或 ‘=’ 之一开头的情况。 当使用本机大小时,已打包值的大小取决于具体的平台。
格式 | C 类型 | Python 类型 | 标准大小 | 注释 |
---|---|---|---|---|
x | 填充字节 | 无 | ||
c | char | 长度为 1 的字节串 | 1 | |
b | signed char | 整数 | 1 | (1), (2) |
B | unsigned char | 整数 | 1 | (2) |
? | _Bool | bool | 1 | (1) |
h | short | 整数 | 2 | (2) |
H | unsigned short | 整数 | 2 | (2) |
i | int | 整数 | 4 | (2) |
I | unsigned int | 整数 | 4 | (2) |
l | long | 整数 | 4 | (2) |
L | unsigned long | 整数 | 4 | (2) |
q | long long | 整数 | 8 | (2) |
Q | unsigned long long | 整数 | 8 | (2) |
n | ssize_t | 整数 | (3) | |
N | size_t | 整数 | (3) | |
e | (6) | 浮点数 | 2 | (4) |
f | float | 浮点数 | 4 | (4) |
d | double | 浮点数 | 8 | (4) |
s | char[] | 字节串 | ||
p | char[] | 字节串 | ||
P | void * | 整数 | (5) |
在 3.3 版更改: 增加了对 ‘n’ 和 ‘N’ 格式的支持
在 3.6 版更改: 添加了对 ‘e’ 格式的支持。
注释:
- ‘?’ 转换码对应于 C99 定义的 _Bool 类型。 如果此类型不可用,则使用 char 来模拟。 在标准模式下,它总是以一个字节表示。
- 当尝试使用任何整数转换码打包一个非整数时,如果该非整数具有 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 编译器广泛支持:在一台典型的机器上,可以使用 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 个字节。
对于 ‘?’ 格式字符,返回值为 True 或 False。 在打包时将会使用参数对象的逻辑值。 以本机或标准 bool 类型表示的 0 或 1 将被打包,任何非零值在解包时将为 True。
5. 示例
注解 所有示例都假定使用一台大端机器的本机字节顺序、大小和对齐方式。
打包/解包三个整数的基础示例:
>>>
>>> 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'
这仅当本机大小和对齐方式生效时才会起作用;标准大小和对齐方式并不会强制进行任何对齐。
6. 类
struct 模块还定义了以下类型:
函数 | 说明 | 备注 |
---|---|---|
class struct.Struct(format) | 返回一个新的 Struct 对象,它会根据格式字符串 format 来写入和读取二进制数据。 一次性地创建 Struct 对象并调用其方法相比使用同样的格式调用 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 对象的格式字符串。 | 在 3.7 版更改: 格式字符串类型现在是 str 而不再是 bytes。 |
size | 计算出对应于 format 的结构大小(亦即 pack() 方法所产生的字节串对象的大小)。 |