struct.unpack() 报“buffer too short”常因忽略内存对齐导致偏移错乱,应使用struct.calcsize()校验真实长度或显式添加padding;变长字段需分步解析,大文件宜用read()分块配合unpack_from()。

直接用 struct.unpack() 解析二进制数据最常用,但效率和健壮性取决于你是否控制好字节序、对齐和缓冲区边界——稍不注意就会解出错值或抛 struct.error: unpack requires a buffer of X bytes。
为什么 unpack() 会报 “buffer too short” 却看起来数据够长?
常见原因是没考虑 C 结构体的内存对齐(padding),比如在 x86_64 上定义:
struct { char a; int b; };实际占用 8 字节(a 后补 3 字节 padding,再放 4 字节 b),但若按 'bi' 解析(1+4=5 字节),就会因跳过 padding 导致后续偏移错乱。正确做法是显式写出填充字节或用 struct.calcsize() 校验:
- 用
struct.calcsize('BxxxI')算出真实长度(B是 1 字节char,xxx是 3 字节 padding,I是 4 字节unsigned int) - 或改用
'=BxxxI'(=表示标准大小 + 无对齐),避免平台默认对齐干扰 - 永远先检查
len(data) >= struct.calcsize(fmt),别信“我数过了”
如何安全处理变长字段(如 C 风格字符串或数组)?
struct 本身不支持动态长度,必须分步解析:先读定长头,再根据其中字段推算后续偏移。例如解析带长度前缀的字符串:
立即学习“Python免费学习笔记(深入)”;
header_fmt = '<I' # 4 字节小端 length
length = struct.unpack(header_fmt, data[:4])[0]
string_bytes = data[4:4+length]
s = string_bytes.rstrip(b'\x00').decode('utf-8') # 注意 null 截断- 不能写
'<Is'——s要求长度已知,否则报错 - 如果字符串可能含 \x00,别用
.rstrip(b'\x00'),改用.split(b'\x00', 1)[0] - 数组同理:先读元素个数
n,再用'<'+str(n)+'I'拼接格式串(注意 str(n) 不加引号)
大文件解析时,怎样避免一次性加载全部数据?
用 file.read(n) 分块读取,配合 struct.unpack_from() 直接从 bytes 片段解析,无需切片复制:
with open('data.bin', 'rb') as f:
while True:
header = f.read(8) # 读 8 字节头
if len(header) < 8:
break
size, tag = struct.unpack_from('<II', header)
body = f.read(size)
if len(body) < size:
raise ValueError('truncated body')
# 处理 body...-
unpack_from(fmt, buffer, offset=0)比unpack(fmt, buffer[offset:offset+N])少一次内存拷贝 - 对超大结构体,用
struct.iter_unpack()替代循环调用unpack_from(),它内部做了缓冲优化 - 别用
memoryview手动管理 offset ——unpack_from已足够高效,且更不易出错
真正难的不是格式串怎么写,而是搞清原始 C 头文件里每个字段的对齐规则、是否有 pragma pack、char 是 signed 还是 unsigned——这些信息漏掉一个,解出来的数据就不可靠。


















