Pythonstruct二进制协议

Pythonstruct二进制协议 Python struct 二进制协议struct 模块在 Python 值和 C 结构体之间打包/解包, 适合二进制协议和文件格式。1. pack / unpack — 基本用法-------------------------------import struct# pack: 将 Python 值打包为字节串# 格式: packed struct.pack( print(pack 结果:, packed) # b\x00\x04print(长度:, len(packed)) # 2# unpack: 将字节串解包为 Python 值value struct.unpack( print(unpack 结果:, value) # (1024,)# 同时打包多个值packed_multi struct.pack(print(多个值打包:, packed_multi.hex()) # 01000000 0200 03# 解析: 4字节(I) 2字节(H) 1字节(B) 7字节# 解包多个值a, b, c struct.unpack(print(f多值解包: I{a}, H{b}, B{c}) # I1, H2, B32. 格式代码详解------------------# 字节顺序前缀:# native 字节序及对齐 (默认)# native 标准大小# 小端序 (little-endian)# 大端序 (big-endian, 网络字节序)# ! 网络字节序 (同 )# 基本格式代码:# x 填充字节 (无对应 Python 值)# b signed char (1字节) - int# B unsigned char - int# h short (2字节) - int# H unsigned short - int# i int (4字节) - int# I unsigned int - int# l long (4/8字节) - int# L unsigned long - int# q long long (8字节) - int# Q unsigned long long - int# f float (4字节) - float# d double (8字节) - float# s char[] - bytes (需要指定长度, 如 10s)# p Pascal 字符串# ? _Bool - bool# 示例:print(int 打包:, struct.pack(i, -100)) # 大端 4 字节print(float 打包:, struct.pack(f, 3.14)) # 大端 4 字节浮点print(double 打包:, struct.pack(d, 3.14159265358979))print(布尔打包:, struct.pack(?, True)) # b\x01# 字符串打包name bPython# 4s 表示固定 4 字节字符串 (截断或补齐空格)print(固定长度字符串:, struct.pack(4s, name)) # bPythprint(完整字符串(带长度):, struct.pack(6s, name)) # bPython3. 字节序 — 小端与大端--------------------------# 小端序: 低地址存低位字节 (x86 架构)# 大端序: 高地址存低位字节 (网络协议)val 0x12345678# 小端: 78 56 34 12little struct.pack( print(小端:, little.hex( )) # 78 56 34 12# 大端: 12 34 56 78big struct.pack(I, val)print(大端:, big.hex( )) # 12 34 56 78# 实战: 从网络数据包读取 2 字节大端端口号raw_port b\x08\xAE # 端口 2222port struct.unpack(!H, raw_port)[0] # ! 和 等价print(网络端口号:, port) # 22224. 位字段处理----------------# struct 不直接支持位字段, 但可通过掩码和移位手动处理# 场景: 解析 IP 首部 (4位版本 4位首部长度)def parse_ip_header_first_byte(data):解析 IP 首部第一个字节: 版本(4bit) 首部长度(4bit)raw struct.unpack(!B, data)[0]version (raw 4) 0x0F # 高4位: 版本号header_len raw 0x0F # 低4位: 首部长度(×4字节)return version, header_len * 4ip_header_first b\x45 # IPv4, 首部长度20字节ver, hdr_len parse_ip_header_first_byte(ip_header_first)print(fIP版本{ver}, 首部长度{hdr_len}字节) # IP版本4, 首部长度20字节# 示例: 解析 2 字节标志位flags_raw struct.pack(H, 0b1011000000000000)flags struct.unpack(H, flags_raw)[0]flag_a (flags 15) 1 # 最高位flag_b (flags 14) 1 # 次高位flag_c (flags 13) 1 # 第三位reserved (flags 12) 1 # 第四位print(f标志位: A{flag_a}, B{flag_b}, C{flag_c}, R{reserved})5. 网络数据包打包/解包--------------------------import structfrom collections import namedtuple# 自定义协议: 固定头部 可变负载# 头部: 版本(1B) 类型(1B) 长度(2B, 大端) 序列号(4B, 大端)PacketHeader namedtuple(PacketHeader, [version, type, length, seq])def pack_packet(version, ptype, seq, payload):打包网络包length len(payload)header struct.pack(!BB HI, version, ptype, length, seq)return header payloaddef unpack_packet(data):解包网络包# 头部固定 8 字节: BBpad(2)HI 11224 8? 不对# H 占 2 字节, I 占 4 字节, 但 BB2pad H 是 4 再 I 是 4, 共 8# 实际上格式 !BB HI 中的空格忽略: B(1)B(1)pad(2)H(2)I(4) 10?# 用 calcsize 确认:hdr_size struct.calcsize(!BB HI)print(包头部大小:, hdr_size) # 8version, ptype, length, seq struct.unpack(!BB HI, data[:hdr_size])payload data[hdr_size:hdr_size length]return PacketHeader(version, ptype, length, seq), payload# 测试发包/解包packet pack_packet(1, 2, 100, bHello Protocol!)header, payload unpack_packet(packet)print(f版本{header.version}, 类型{header.type}, 长度{header.length}, 序列号{header.seq})print(f负载内容: {payload})# 注意: struct 在打包 B B 后可能插入 2 字节填充以满足 H 的对齐要求# 用 (native) 前缀时填充有平台相关性, ! 或 或 则不填充print(无填充格式大小:, struct.calcsize(!BBHI)) # 86. 读取/写入二进制文件格式-----------------------------import struct# 场景: 读取 BMP 位图文件头# BMP 文件头 (14字节):# bfType(2B) bfSize(4B) bfReserved1(2B) bfReserved2(2B) bfOffBits(4B)bmp_header_format bmp_header_size struct.calcsize(bmp_header_format)print(BMP 头部大小:, bmp_header_size) # 14# 模拟读取 BMP 头def parse_bmp_header(data):解析 BMP 文件头if len(data) 14:raise ValueError(数据不足14字节)bf_type, bf_size, _, _, bf_off_bits struct.unpack_from(bmp_header_format, data, 0)# bf_type 应为 0x4D42 (即 BM)if bf_type ! 0x4D42:raise ValueError(f不是有效的 BMP 文件: 签名{bf_type:04X})return {大小: bf_size, 数据偏移: bf_off_bits}# 构造模拟 BMP 数据mock_bmp struct.pack(bmp_header_format, 0x4D42, 1024, 0, 0, 54)info parse_bmp_header(mock_bmp)print(BMP 文件信息:, info) # {大小: 1024, 数据偏移: 54}7. struct.calcsize — 计算大小与对齐---------------------------------------import struct# calcsize 根据格式字符串计算打包后的字节大小print(!i2f 大小:, struct.calcsize(!i2f)) # 44412print(i2f 大小(原生):, struct.calcsize(i2f)) # 可能不同 (平台相关)# native 对齐规则:# char (B) 对齐 1 字节# short (H) 对齐 2 字节# int (I) 对齐 4 字节# long long 对齐 8 字节print(iB 大小:, struct.calcsize(iB)) # 8 (int 4 char 1 填充3)print(iB 大小(标准):, struct.calcsize(iB)) # 5 (无填充)print(!iB 大小:, struct.calcsize(!iB)) # 5 (无填充)# 使用 pack_into / unpack_from 避免创建中间 bytes 对象import ctypesbuf ctypes.create_string_buffer(20) # 预分配缓冲区struct.pack_into(!i H B, buf, 0, 100, 200, 50) # 打包到指定偏移i, h, b struct.unpack_from(!i H B, buf, 0)print(fpack_into/unpack_from: i{i}, h{h}, b{b})# 性能对比: 频繁打包时应复用格式字符串# 缓存格式字符串可避免重复编译import timefmt !I H B 10scompiled struct.Struct(fmt) # 预编译格式start time.perf_counter()for _ in range(10000):compiled.pack(1, 2, 3, btest) # 使用预编译对象print(预编译 Struct 性能: 10000 次用时(ms),(time.perf_counter() - start) * 1000)# Struct 对象也支持 pack_into / unpack_frombuf bytearray(compiled.size)compiled.pack_into(buf, 0, 42, 7, 1, bdata)values compiled.unpack_from(buf, 0)print(Struct 对象解析:, values)总结: struct 是二进制协议开发的核心模块, 配合 BytesIO / memoryview 可实现高效数据处理.