Python struct.pack 用法总结
·
目录
概述
struct.pack 是 Python 中用于将数据打包成字节流的强大工具,特别适用于处理二进制数据、网络协议、文件格式等场景。通过掌握 struct.pack,可以高效地在 Python 中处理各种二进制数据场景。
1 struct.pack语法
1.1 基本语法
struct.pack(format, v1, v2, ...)
format:格式字符串,指定如何打包数据
v1, v2, ...:要打包的值返回值:打包后的字节对象
1.2 格式字符
1) 字节顺序/大小/对齐字符
| 字符 | 字节顺序 | 大小 | 对齐 |
|---|---|---|---|
@ |
原生 | 原生 | 原生 |
= |
原生 | 标准 | 无 |
< |
小端 | 标准 | 无 |
> |
大端 | 标准 | 无 |
! |
网络(大端) | 标准 | 无 |
2) 数据类型格式字符
| 格式 | C 类型 | Python 类型 | 标准大小 |
|---|---|---|---|
x |
填充字节 | - | 1 |
c |
char | 长度为1的字节串 | 1 |
b |
signed char | int | 1 |
B |
unsigned char | int | 1 |
? |
_Bool | bool | 1 |
h |
short | int | 2 |
H |
unsigned short | int | 2 |
i |
int | int | 4 |
I |
unsigned int | int | 4 |
l |
long | int | 4 |
L |
unsigned long | int | 4 |
q |
long long | int | 8 |
Q |
unsigned long long | int | 8 |
f |
float | float | 4 |
d |
double | float | 8 |
s |
char[] | bytes | - |
p |
char[] | bytes | - |
P |
void * | int | - |
1.3 应用要点
struct.pack的主要用途:
网络编程:打包协议数据包
文件格式:处理二进制文件格式
硬件交互:与设备寄存器通信
数据序列化:将结构化数据转换为字节流
跨平台数据交换:处理不同字节顺序
关键要点:
始终明确指定字节顺序(
<,>,!)使用
struct.calcsize()验证格式大小处理字符串时注意编码和长度
考虑平台差异,特别是在处理原生格式时
适当的错误处理对于生产代码很重要
2 应用示例
2.1 基本示例
import struct
# 基本数值打包
print("=== 基本数值打包 ===")
# 打包整数
data = struct.pack('i', 123) # 4字节整数
print(f"整数 123: {data} -> {list(data)}")
# 打包多个值
data = struct.pack('ii', 123, 456) # 两个4字节整数
print(f"两个整数: {data}")
# 打包不同数据类型
data = struct.pack('i f', 100, 3.14) # 整数 + 浮点数
print(f"整数和浮点数: {data}")
# 使用计数
data = struct.pack('3i', 1, 2, 3) # 3个整数
print(f"3个整数: {data}")
2.2 字节顺序示例
import struct
print("\n=== 字节顺序示例 ===")
value = 0x12345678
# 不同字节顺序
little_endian = struct.pack('<I', value) # 小端
big_endian = struct.pack('>I', value) # 大端
native = struct.pack('@I', value) # 原生
network = struct.pack('!I', value) # 网络字节序(大端)
print(f"原始值: 0x{value:08X}")
print(f"小端: {little_endian.hex()} -> {list(little_endian)}")
print(f"大端: {big_endian.hex()} -> {list(big_endian)}")
print(f"原生: {native.hex()} -> {list(native)}")
print(f"网络: {network.hex()} -> {list(network)}")
2.3 字符串和字节处理
import struct
print("\n=== 字符串和字节处理 ===")
# 固定长度字符串
name = b"Hello"
data = struct.pack('5s', name) # 5字节字符串
print(f"固定字符串: {data} -> {data.decode('ascii')}")
# 带长度的字符串(Pascal字符串)
data = struct.pack('5p', b"Hello") # 1字节长度 + 字符串
print(f"Pascal字符串: {data}")
# 空字符结尾的字符串(C字符串)
data = struct.pack('6s', b"Hello\x00")
print(f"C字符串: {data}")
2.4 复杂结构示例
import struct
print("\n=== 复杂结构示例 ===")
# 模拟网络数据包
def create_packet(version, packet_type, length, data):
"""创建网络数据包"""
# 格式: 版本(1B) | 类型(1B) | 长度(2B) | 数据(变长)
header = struct.pack('!BBH', version, packet_type, length)
packet = header + data
return packet
# 创建数据包
version = 1
packet_type = 10
data = b"Hello, World!"
length = len(data)
packet = create_packet(version, packet_type, length, data)
print(f"数据包: {packet.hex()}")
print(f"数据包长度: {len(packet)} 字节")
# 解析数据包
def parse_packet(packet):
"""解析网络数据包"""
header = packet[:4] # 前4字节是头部
data = packet[4:] # 剩余是数据
version, packet_type, length = struct.unpack('!BBH', header)
return version, packet_type, length, data
v, t, l, d = parse_packet(packet)
print(f"解析结果: 版本={v}, 类型={t}, 长度={l}, 数据={d.decode()}")
3 实际应用案例
3.1 处理二进制文件格式
import struct
print("\n=== 二进制文件处理 ===")
# 模拟BMP文件头(简化版)
def create_bmp_header(width, height):
"""创建BMP文件头"""
file_size = 54 + width * height * 3 # 头54字节 + RGB数据
# BMP文件头 (14字节)
file_header = struct.pack('<2sIHHI',
b'BM', # 签名
file_size, # 文件大小
0, 0, # 保留
54) # 数据偏移
# BMP信息头 (40字节)
info_header = struct.pack('<IIIHHIIIIII',
40, # 信息头大小
width, # 宽度
height, # 高度
1, # 平面数
24, # 每像素位数 (24-bit RGB)
0, # 压缩方式
width * height * 3, # 图像数据大小
0, 0, 0, 0) # 分辨率等信息
return file_header + info_header
# 创建BMP头
bmp_header = create_bmp_header(100, 100)
print(f"BMP头长度: {len(bmp_header)} 字节")
print(f"BMP头: {bmp_header.hex()}")
3.2 网络协议实现
import struct
import socket
print("\n=== 网络协议示例 ===")
# 模拟IP头部(简化)
def create_ip_header(source_ip, dest_ip, data):
"""创建IP头部"""
version_ihl = 0x45 # IPv4, 头部长度5字 (20字节)
tos = 0 # 服务类型
total_length = 20 + len(data) # 总长度
identification = 54321 # 标识
flags_fragment = 0 # 标志和片偏移
ttl = 64 # 生存时间
protocol = 6 # TCP
checksum = 0 # 校验和(简化)
# 转换IP地址
src = socket.inet_aton(source_ip)
dst = socket.inet_aton(dest_ip)
# 打包IP头部
ip_header = struct.pack('!BBHHHBBH4s4s',
version_ihl, tos, total_length,
identification, flags_fragment,
ttl, protocol, checksum,
src, dst)
return ip_header
# 创建IP数据包
ip_header = create_ip_header("192.168.1.1", "192.168.1.2", b"Hello")
print(f"IP头部: {ip_header.hex()}")
3.3 硬件寄存器操作
import struct
print("\n=== 硬件寄存器操作 ===")
# 模拟设备控制寄存器
def create_device_control_register(enable, mode, speed, address):
"""创建设备控制寄存器值"""
# 假设寄存器布局:
# Bit 31-24: 地址
# Bit 23-16: 速度
# Bit 15-8: 模式
# Bit 7-0: 控制标志
control = 0
if enable:
control |= 0x01 # 启用位
control |= (mode & 0xFF) << 8
control |= (speed & 0xFF) << 16
control |= (address & 0xFF) << 24
# 打包为4字节
data = struct.pack('<I', control)
return data, control
# 创建设备控制命令
data, value = create_device_control_register(True, 2, 100, 0xAB)
print(f"控制寄存器值: 0x{value:08X}")
print(f"打包数据: {data.hex()} -> {list(data)}")
3.4 高级技巧
1) 使用 calcsize 计算大小
import struct
print("\n=== 计算格式大小 ===")
formats = ['i', 'f', 'd', '10s', 'if', '3i', '<i', '>i']
for fmt in formats:
size = struct.calcsize(fmt)
print(f"格式 '{fmt}' 需要 {size} 字节")
2) 处理填充和对齐
import struct
print("\n=== 填充和对齐 ===")
# 有填充的结构
data_with_pad = struct.pack('b h i', 1, 2, 3)
print(f"有填充: {data_with_pad.hex()} -> {len(data_with_pad)} 字节")
# 无填充的结构(使用标准大小和对齐)
data_no_pad = struct.pack('=b h i', 1, 2, 3)
print(f"无填充: {data_no_pad.hex()} -> {len(data_no_pad)} 字节")
3) 错误处理
import struct
print("\n=== 错误处理 ===")
try:
# 值超出范围
data = struct.pack('B', 300) # unsigned byte 最大255
except struct.error as e:
print(f"错误: {e}")
try:
# 参数数量不匹配
data = struct.pack('ii', 1) # 需要2个整数
except struct.error as e:
print(f"错误: {e}")
更多推荐


所有评论(0)