struct —— 二进制数据结构
2017-07-05 本文已影响313人
lakerszhy
原文地址:https://pymotw.com/3/struct/
目的:在字符串和二进制数据之间转换。
struct
模块包括用于在字节字符串和Python原生数据类型(比如数字和字符串)之间转换的函数。
函数 vs Struct类
一组模块级别的函数和Struct
类都可以用于结构化值。格式说明符从它的字符串格式转换为编译后的描述,类似于处理正则表达式的方式。转换需要一些资源,所以创建一个Struct
实例并在实例上调用方法,比使用模块级别的函数更高效。以下所有示例都使用Struct
类。
打包和拆包
struct
使用由字符串组成的格式说明符表示数据类型和可选的计数与字节顺序指示符,支持将数据打包为字符串,以及从字符串拆包数据。支持的完整格式说明符列表请参考标准库文档。
这个示例中,说明符需要一个整数或长整数值,一个双字节字符串和一个浮点数。格式说明符中的空格用于分隔类型指示符,在编译格式时会被忽略。
# struct_pack.py
import struct
import binascii
values = (1, 'ab'.encode('utf-8'), 2.7)
s = struct.Struct('I 2s f')
packed_data = s.pack(*values)
print('Original values:', values)
print('Format String :', s.format)
print('Uses :', s.size, 'bytes')
print('Packed Value :', binascii.hexlify(packed_data))
因为有些字符为空,为了使用binascii.hexlify()
打印,该示例把打包的值转换为十六进制字节序列。
$ python3 struct_pack.py
Original values: (1, b'ab', 2.7)
Format string : b'I 2s f'
Uses : 12 bytes
Packed Value : b'0100000061620000cdcc2c40'
使用unpack()
从它打包表示中提取数据。
# struct_unpack.py
import struct
import binascii
packed_data = binascii.unhexlify(b'0100000061620000cdcc2c40')
s = struct.Struct('I 2s f')
unpacked_data = s.unpack(packed_data)
print('Unpacked Values:', unpacked_data)
把打包的数据传递给unpack()
,会返回基本相同的值(注意浮点值的差异)。
$ python3 struct_unpack.py
Unpacked Values: (1, b'ab', 2.700000047683716)
字节顺序
默认情况下,值使用本机C库的字节顺序编码。通过在格式化字符串中提供明确的字节顺序指令,可以很容易地覆盖该选择。
# struct_endianness.py
import struct
import binascii
values = (1, 'ab'.encode('utf-8'), 2.7)
print('Original values:', values)
endianness = [
('@', 'native, native'),
('=', 'native, standard'),
('<', 'little-endian'),
('>', 'big-endian'),
('!', 'network'),
]
for code, name in endianness:
s = struct.Struct(code + ' I 2s f')
packed_data = s.pack(*values)
print()
print('Format string :', s.format, 'for', name)
print('Uses :', s.size, 'bytes')
print('Pakced Value :', binascii.hexlify(packed_data))
print('Unpacked Value :', s.unpack(packed_data))
下表列出了Struct
使用字节顺序说明符。
代码 | 意义 |
---|---|
@ | Native order |
= | Native standard |
< | little-endian |
> | big-endian |
! | Network order |
$ python3 struct_endianness.py
Original values: (1, b'ab', 2.7)
Format string : b'@ I 2s f' for native, native
Uses : 12 bytes
Packed Value : b'0100000061620000cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)
Format string : b'= I 2s f' for native, standard
Uses : 10 bytes
Packed Value : b'010000006162cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)
Format string : b'< I 2s f' for little-endian
Uses : 10 bytes
Packed Value : b'010000006162cdcc2c40'
Unpacked Value : (1, b'ab', 2.700000047683716)
Format string : b'> I 2s f' for big-endian
Uses : 10 bytes
Packed Value : b'000000016162402ccccd'
Unpacked Value : (1, b'ab', 2.700000047683716)
Format string : b'! I 2s f' for network
Uses : 10 bytes
Packed Value : b'000000016162402ccccd'
Unpacked Value : (1, b'ab', 2.700000047683716)
缓冲区
使用二进制打包数据通常用于性能敏感情况,或者把数据传出或传入扩展模块。这些情况下,通过避免为每个被打包的结构分配新缓冲区的开销来进行优化。pack_into()
和unpack_from()
函数支持直接写入预分配的缓冲区。
# struct_buffers.py
import array
import binascii
import ctypes
import struct
s = struct.Struct('I 2s f')
values = (1, 'ab'.encode('utf-8'), 2.7)
print('Original:', values)
print()
print('ctypes string buffer')
b = ctypes.create_string_buffer(s.size)
print('Befor :', binascii.hexlify(b.raw))
s.pack_into(b, 0, *values)
print('After :', binascii.hexlify(b.raw))
print('Unpacked:', s.unpack_from(b, 0))
print()
print('array')
a = array.array('b', b'\0' * s.size)
print('Befor :', binascii.hexlify(a))
s.pack_into(a, 0, *values)
print('After :', binascii.hexlify(a))
print('Unpacked:', s.unpack_from(a, 0))
Struct
的size
属性告诉我们需要多大的缓冲区。
$ python3 struct_buffers.py
Original: (1, b'ab', 2.7)
ctypes string buffer
Before : b'000000000000000000000000'
After : b'0100000061620000cdcc2c40'
Unpacked: (1, b'ab', 2.700000047683716)
array
Before : b'000000000000000000000000'
After : b'0100000061620000cdcc2c40'
Unpacked: (1, b'ab', 2.700000047683716)
参考
- struct的标准库文档
- struct的Python2到3的移植笔记
- array ——
array
模块,用于固定类型的值序列。- binascii ——
binascii
模块,用于产生二进制数据的ASCII表示。- 维基百科:字节顺序 —— 编码中的字节顺序说明。