目录

概述

1  struct.pack语法

1.1 基本语法

1.2 格式字符

1.3 应用要点

2 应用示例

2.1 基本示例

2.2 字节顺序示例

2.3  字符串和字节处理

2.4 复杂结构示例

3 实际应用案例

3.1 处理二进制文件格式

3.2  网络协议实现

3.3 硬件寄存器操作

3.4 高级技巧


概述

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 的主要用途:

  1. 网络编程:打包协议数据包

  2. 文件格式:处理二进制文件格式

  3. 硬件交互:与设备寄存器通信

  4. 数据序列化:将结构化数据转换为字节流

  5. 跨平台数据交换:处理不同字节顺序


关键要点:

  • 始终明确指定字节顺序(<>!

  • 使用 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}")

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐