Python struct.pack() 函数详解

简介

struct.pack() 是 Python 标准库中 struct 模块的一个函数,主要用于将 Python 数据转换为字节串,这些字节串符合特定的格式,便于二进制数据的存储和网络传输。此功能在处理网络通信和二进制文件时尤为重要。

功能原理

struct.pack() 函数通过格式字符串指定预期的结构,按照指定格式将 Python 数据(如数字、字符串、布尔值等)打包成字节串。格式字符串为一系列的格式字符,每个字符代表一种数据类型,同时决定了数据在编码后的二进制表示。

格式字符

下表列出了一些常用的格式字符,以及它们对应的数据类型和字节大小:

格式字符数据类型标准大小
bsigned char1
Bunsigned char1
hshort2
Hunsigned short2
iint4
Iunsigned int4
ffloat4
ddouble8
schar[]1

常见用途

1. 网络通信:在网络通信中,数据需要被序列化为字节串才能进行传输。struct.pack() 提供了一种方便的方式来将各种数据类型转换成字节串。
2. 文件存储:在写入二进制文件时,可以使用 struct.pack() 将需要存储的数据转换为字节串。

示例代码

以下是一些 struct.pack() 的基本用法示例,展示如何将不同类型的数据打包成字节串。

打包整数
import struct

# 打包一个整数
result = struct.pack('i', 1024)
print(result)  # 输出:b'\x00\x04\x00\x00'(在小端字节序的系统中)
打包浮点数
示例
# 打包一个浮点数
result = struct.pack('f', 3.14)
print(result)  # 输出类似:b'\xc3\xf5H@'(具体输出可能因系统而异)
解释

输出结果是以16进制表示的二进制数据。在Python中,当你使用 struct.pack() 函数将数据(如浮点数)打包成字节串时,返回的字节串中的每个字节会以16进制的形式显示,每对16进制数字代表一个字节。例如,b'\xc3\xf5H@' 中:

  • \xc3 表示一个字节,16进制的 C3 是它的值。
  • \xf5 也是一个字节,16进制的 F5 是它的值。
  • H@ 是这个字节串可打印字符的直接显示。当字节的值在ASCII可打印字符的范围内(0x20到0x7E),它们就以字符形式显示。

这种表示方式允许你直观地看到数据在内存中的二进制表示,这对于调试和处理需要精确数据表示的应用(如网络通信或文件格式定义)非常有用。

理解字节串 b'\xc3\xf5H@' 如何代表浮点数 3.14 需要涉及到浮点数在计算机中的存储方式,具体为 IEEE 754
标准。这个标准定义了浮点数的二进制表示格式,其中单精度(float,通常占用4字节)的浮点数的布局如下:

  • 符号位:1位,决定数值的正负。
  • 指数位:8位,决定数值的大小范围。
  • 尾数位:23位,存储实际数字信息。

3.14 转换为二进制格式时,计算机会按照上述规则将其转换成对应的二进制序列。在大多数系统中(假定使用 IEEE 754
标准和小端字节序),浮点数 3.14 的二进制表示大致过程如下:

  1. 将3.14转换为IEEE 754格式的二进制表示

    • 3.14的二进制近似表示为 11.00100011110101110000101(这里只显示了部分尾数位,实际上有更多)。
    • 将这个二进制数标准化后,得到 1.100100011110101110000101 x 2^1
    • 符号位为0(因为是正数)。
    • 指数位为 1 + 127 = 128(127是偏移量),二进制表示为 10000000
    • 尾数位为 100100011110101110000101(忽略前面的隐含的1)。
  2. 整合符号位、指数位和尾数位

    • 组合成:0 | 10000000 | 100100011110101110000101
  3. 转换为十六进制

    • 这个32位的二进制数转换为十六进制,通常你会得到类似 4048F5C3 的形式,但实际上具体值会根据处理器架构的不同而略有不同。在小端序架构中,字节顺序会反转,所以可能显示为 C3F54840
  4. 在Python中的显示

    • 在Python中,使用 struct.pack('f', 3.14) 打包时,默认使用小端序,因此十六进制的字节顺序可能显示为 b'\xc3\xf5H@'

理解这个过程需要一定的底层二进制和内存表示知识。每个步骤中的具体值和表示可能根据使用的系统和编程环境(如编译器或解释器的具体实现)略有不同。

打包多种数据类型
# 打包多种数据类型
result = struct.pack('iif', 1024, 2048, 3.14)
print(result)  # 输出类似:b'\x00\x04\x00\x00\x00\x08\x00\x00\xc3\xf5H@'

高级应用

字节对齐和填充

在打包结构体时,有时需要对齐字节以符合特定的内存对齐要求。struct.pack() 通过在格式字符串前加上对齐符,可以实现这一功能。

# 4字节对齐
result = struct.pack('iif', 1024, 2048, 3.14)
print(result)  # 输出将根据对齐符而可能不同
使用网络字节序

网络传输时常常需要数据以网络字节序(大端序)进行序列化。可以通过在格式字符串前加 ! 实现这一点。

# 使用网络字节序
result = struct.pack('!iif', 1024, 2048, 3.14)
print(result)  # 输出字节序为大端序

结论

struct.pack() 是一个强大的工具,适用于需要数据打包为二进制格式的多种场景。通过合理使用格式字符和对齐方式,可以有效地处理各种数据类型的序列化问题。

其他示例

示例1:ModbusRTU报文组包>B B H H

def build_request_06(self, device_id: int, start_address: int, value: int) -> bytes:
    """
    构建写单个寄存器(功能码 06)的 Modbus RTU 请求。
    Args:
        device_id (int): 设备 ID。
        start_address (int): 起始地址。
        value (int): 寄存器的值。
    Returns:
        bytes: 完整的 Modbus RTU 请求数据,包括 CRC 校验。
    """
    header_format = '>B B H H'
    header = struct.pack(header_format,
                         device_id,
                         6,
                         start_address,
                         value)
    request = header
    request += self.crc16(request)
    return request

解释:

在Python的 struct.pack 函数中,字符串 '>B B H H' 用于定义如何将数据转换为字节。这个格式字符串包含两部分:字节顺序/对齐和类型代码。下面解释这个字符串中的每一个部分:

  1. >:这个符号代表 “big-endian” 字节顺序。big-endian 是一种在内存中排列多字节数据类型(如整数、浮点数)的方法,其中最重要的字节(最高位字节)存储在最低的内存地址上。这种格式在网络通信中非常常见,因为它是网络传输标准的一部分。

  2. B:表示一个无符号字符(unsigned char),占用1个字节。在这个上下文中,B 用于表示单字节的数值,如设备 ID 或功能码。

  3. H:表示一个无符号短整数(unsigned short),占用2个字节。在这里,它用于表示需要更多字节(如两个)的数值,例如起始地址和寄存器的值。

因此,当你看到 '>B B H H' 时,它指的是:

  • 第一个 B:设备ID,1个字节
  • 第二个 B:功能码,1个字节
  • 第一个 H:起始地址,2个字节
  • 第二个 H:寄存器的值,2个字节

这种格式确保了当你构建一个Modbus RTU请求时,各个部分都将按照预期的大小和顺序被正确地打包成字节流。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐