《详细介绍Python使用struct处理二进制(pack和unpack用法)》
在Python中处理二进制数据是许多场景下的核心需求,例如网络通信、文件解析、硬件交互等。二进制数据以紧凑的字节形式存储,但直接操作字节流需要处理字节顺序、数据类型对齐等复杂问题。Python的`struct`模块提供了高效的解决方案,通过`pack`和`unpack`函数将Python基本数据类型与二进制字节相互转换,极大简化了二进制数据的处理流程。
一、struct模块基础
`struct`模块的核心功能是按照指定的格式将Python数据转换为C语言结构的二进制表示(`pack`),或从二进制数据中解析出Python对象(`unpack`)。其核心函数包括:
-
struct.pack(fmt, v1, v2, ...)
:将Python值按格式`fmt`打包为字节对象 -
struct.unpack(fmt, buffer)
:从字节对象`buffer`中按格式`fmt`解包为Python元组 -
struct.calcsize(fmt)
:返回格式`fmt`对应的字节长度
1.1 格式字符串(Format String)
格式字符串由两部分组成:字节序标记和数据类型代码。例如:
'
表示:
- `
- `i`:有符号整数(4字节)
- `i`:有符号整数(4字节)
- `h`:有符号短整型(2字节)
完整字节序标记:
- `@`:原生字节序(默认,依赖系统)
- `=`:原生字节序,标准大小
- `
- `>`:大端字节序(Big-endian)
- `!`:网络字节序(等同于`>`)
常用数据类型代码:
代码 | C类型 | Python类型 | 字节数 |
---|---|---|---|
x | 填充字节 | 无 | 1 |
c | char | bytes(长度1) | 1 |
b | signed char | 整数 | 1 |
B | unsigned char | 整数 | 1 |
? | _Bool | 布尔值 | 1 |
h | short | 整数 | 2 |
H | unsigned short | 整数 | 2 |
i | int | 整数 | 4 |
I | unsigned int | 整数 | 4 |
l | long | 整数 | 4 |
L | unsigned long | 整数 | 4 |
q | long long | 整数 | 8 |
Q | unsigned long long | 整数 | 8 |
f | float | 浮点数 | 4 |
d | double | 浮点数 | 8 |
s | char[] | bytes | 指定长度 |
p | char[] | bytes | 指定长度+1(含终止符) |
P | void * | 整数 | 与系统指针同大小 |
二、pack函数详解
`pack`函数将Python值转换为二进制字节。其语法为:
struct.pack(fmt, v1, v2, ...)
2.1 基本用法
示例:打包两个整数和一个浮点数
import struct
data = struct.pack('
解释:
- `
- 输出为12字节的二进制数据
2.2 处理字符串
打包字符串时需指定长度:
# 打包5字节字符串
packed = struct.pack('
2.3 对齐与填充
某些架构要求数据按特定边界对齐。使用`@`或`=`可启用原生对齐:
# 原生对齐(可能包含填充字节)
packed = struct.pack('@i d', 1, 2.5)
print(len(packed)) # 输出12(4字节int + 4字节填充 + 8字节double)
三、unpack函数详解
`unpack`函数将二进制字节解析为Python元组。其语法为:
struct.unpack(fmt, buffer)
3.1 基本用法
示例:解包之前打包的数据
import struct
packed = b'\n\x00\x00\x00\x14\x00\x00\x00@\t\x1e\xb8'
unpacked = struct.unpack('
3.2 处理变长数据
解包时需确保字节长度与格式匹配:
# 解包错误示例(字节长度不足)
try:
struct.unpack('
3.3 动态格式字符串
可通过`calcsize`验证格式长度:
fmt = '
四、高级应用场景
4.1 网络协议解析
解析TCP头部的简化示例:
import struct
# TCP头部格式(简化版):源端口(2B)、目的端口(2B)、序列号(4B)
tcp_header = b'\x1f\x90\x08\x00\x12\x34\x56\x78'
src_port, dst_port, seq_num = struct.unpack('!HHI', tcp_header)
print(f"源端口: {src_port}, 目的端口: {dst_port}, 序列号: {seq_num}")
# 输出:源端口: 8080, 目的端口: 2048, 序列号: 305419896
4.2 二进制文件读写
写入和读取自定义二进制文件:
# 写入文件
with open('data.bin', 'wb') as f:
packed = struct.pack('
4.3 与数组模块结合
处理大量二进制数据时,可结合`array`模块提高效率:
import struct
import array
# 生成100个浮点数
floats = (i * 0.1 for i in range(100))
# 打包为二进制
packed = b''.join(struct.pack('
五、常见问题与解决方案
5.1 字节序问题
不同系统可能使用不同字节序,网络通信中建议统一使用大端序:
# 明确指定网络字节序
data = struct.pack('!I', 0x12345678)
print(data) # 输出:b'\x124Vx'(大端序)
5.2 浮点数精度
浮点数打包解包可能存在精度损失:
original = 3.141592653589793
packed = struct.pack('
5.3 性能优化
对于大量数据,避免循环调用`pack`/`unpack`:
# 低效方式
results = []
for i in range(1000):
results.append(struct.pack('
六、完整示例:自定义二进制协议
设计一个包含头部和数据的简单协议:
import struct
# 协议格式:
# 魔数(4B) | 版本(1B) | 类型(1B) | 数据长度(2B) | 数据(...)
PROTOCOL_FMT = '
关键词:Python、struct模块、二进制处理、pack函数、unpack函数、格式字符串、字节序、网络协议、数据解析、性能优化
简介:本文详细介绍了Python中struct模块处理二进制数据的方法,重点讲解了pack和unpack函数的用法,包括格式字符串的组成、字节序控制、数据类型转换等核心概念。通过实际案例展示了在网络协议解析、二进制文件读写等场景中的应用,并提供了常见问题的解决方案和性能优化技巧。