Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

struct 模块用于在 Python 值与 C 结构体之间进行转换,常用于处理二进制数据(如文件、网络协议等)。以下是主要用法:

1. 基本格式字符

常用类型代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import struct

# 基本类型
b = struct.pack('b', -1) # 有符号字符
B = struct.pack('B', 255) # 无符号字符
h = struct.pack('h', -32768) # 短整型
H = struct.pack('H', 65535) # 无符号短整型
i = struct.pack('i', -1) # 整型
I = struct.pack('I', 4294967295) # 无符号整型
q = struct.pack('q', -1) # 长整型
Q = struct.pack('Q', 18446744073709551615) # 无符号长整型
f = struct.pack('f', 3.14) # 单精度浮点
d = struct.pack('d', 3.14159) # 双精度浮点
s = struct.pack('5s', b'hello') # 字节串(固定长度)

与C/C++类型的对应关系

Python struct C/C++ 类型 条件/平台 大小(字节)
b int8_t 所有平台 1
B uint8_t 所有平台 1
h int16_t 所有平台 2
H uint16_t 所有平台 2
i int32_t 所有平台 4
l int32_tint64_t 平台相关 32位系统为 4 字节,64位系统通常为 8 字节
I uint32_t 所有平台 4
L uint32_tuint64_t 平台相关 32位系统为 4 字节,64位系统通常为 8 字节
q int64_t 所有平台 8
Q uint64_t 所有平台 8
n ssize_tintptr_t 平台相关 Python 3.3+之后才支持。32位系统为 4 字节,64位系统通常为 8 字节
N size_tuintptr_t 平台相关 Python 3.3+之后才支持。32位系统为 4 字节,64位系统通常为 8 字节
P void*uintptr_t 平台相关 指针数据,32位系统为 4 字节,64位系统通常为 8 字节
s char[] 所有平台 固定长度的字符串,如10s表示10个字符的字符串

2. 基本操作

打包(pack)

1
2
3
4
5
import struct

# 打包数据
data = struct.pack('i f 5s', 10, 3.14, b'hello')
print(data) # b'\n\x00\x00\x00\xc3\xf5H@hello'

解包(unpack)

1
2
3
4
5
6
7
8
# 解包数据
packed_data = struct.pack('i f 5s', 10, 3.14, b'hello')
values = struct.unpack('i f 5s', packed_data)
print(values) # (10, 3.141592502593994, b'hello')

# 使用 calcsize 获取大小
size = struct.calcsize('i f 5s')
print(f"Size: {size} bytes") # 4 + 4 + 5 = 13 bytes

3. 字节序与对齐

字节序前缀

  • @:原生(默认)
  • <:小端(x86, ARM LE)
  • >:大端(网络字节序,PowerPC)
  • !:网络字节序(同 >

举例:

1
2
3
4
5
6
7
8
9
10
11
12
# 不同字节序示例
import struct

value = 0x12345678

native = struct.pack('I', value) # 原生顺序
little = struct.pack('<I', value) # 小端:78 56 34 12
big = struct.pack('>I', value) # 大端:12 34 56 78
network = struct.pack('!I', value) # 网络字节序(同大端)

print(f"Little endian: {little.hex()}")
print(f"Big endian: {big.hex()}")

4. 高级用法

使用 Struct 对象(多次使用相同格式时更高效)

1
2
3
4
5
6
7
8
9
10
11
import struct

# 创建 Struct 对象
s = struct.Struct('i f 5s')

# 打包
packed = s.pack(10, 3.14, b'hello')

# 解包
unpacked = s.unpack(packed)
print(unpacked) # (10, 3.141592502593994, b'hello')

处理变长数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 动态长度字符串
def pack_string(s):
data = s.encode('utf-8')
length = len(data)
return struct.pack(f'I {length}s', length, data)

def unpack_string(data):
length, = struct.unpack('I', data[:4])
s, = struct.unpack(f'{length}s', data[4:4+length])
return s.decode('utf-8')

# 使用
packed = pack_string("Hello World!")
print(unpack_string(packed)) # Hello World!

5. 实际应用示例

示例1:解析 BMP 文件头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import struct

def parse_bmp_header(filename):
with open(filename, 'rb') as f:
# 读取前54字节的BMP头
data = f.read(54)

# 解析字段
# 2s: 文件类型,I: 文件大小,H H: 保留字段,I: 像素数据偏移
header = struct.unpack('<2s I H H I', data[:14])
file_type, file_size, _, _, data_offset = header

# 解析信息头
info = struct.unpack('<I i i H H I I I I I I', data[14:54])
header_size, width, height, planes, bits_per_pixel = info[:5]

return {
'file_type': file_type.decode(),
'width': width,
'height': height,
'bits_per_pixel': bits_per_pixel
}

示例2:网络协议数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import struct
import socket

def create_packet(seq_num, data):
"""创建简单的数据包:序号(4字节) + 长度(4字节) + 数据"""
data_bytes = data.encode('utf-8')
length = len(data_bytes)
return struct.pack('!I I', seq_num, length) + data_bytes

def parse_packet(packet):
"""解析数据包"""
seq_num, length = struct.unpack('!I I', packet[:8])
data = packet[8:8+length].decode('utf-8')
return seq_num, data

# 使用
packet = create_packet(1, "Hello, World!")
print(parse_packet(packet)) # (1, "Hello, World!")

示例3:处理二进制数据文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import struct

# 写入二进制数据
with open('data.bin', 'wb') as f:
for i in range(5):
f.write(struct.pack('i f', i, i * 1.5))

# 读取二进制数据
with open('data.bin', 'rb') as f:
while True:
chunk = f.read(8) # i(4字节) + f(4字节)
if not chunk:
break
i, f_val = struct.unpack('i f', chunk)
print(f"i={i}, f={f_val}")

6. 常见格式字符串模式

1
2
3
4
5
6
7
8
9
# 常用组合
format_strings = {
'int_float': 'i f', # 整数 + 浮点数
'rgb_pixel': 'BBB', # RGB像素 (3个无符号字节)
'rgba_pixel': 'BBBB', # RGBA像素
'vector3': 'f f f', # 3D向量
'header': '<I I 16s', # 带魔数的文件头
'network_packet': '!H H I', # 网络包(源端口、目标端口、序列号)
}

注意事项

  1. 字节序很重要:确保打包和解包使用相同的字节序
  2. 大小端问题:x86是小端,网络传输通常用大端
  3. 内存对齐:C结构体可能有对齐,使用 @ 格式时会考虑对齐
  4. Python整数无大小限制,但打包到固定大小时可能溢出
  5. 字符串处理s 格式用于字节串,不是Unicode字符串

通过 struct 模块,Python可以方便地与底层二进制数据进行交互,这在处理文件格式、网络协议、硬件接口等方面非常有用。

评论