Python struct模块中pack与unpack的使用详解

星野
星野
发布时间:2026-10-13| 3人浏览过
原创

本文详细介绍Python中struct模块的pack和unpack函数,用于不同编程语言及硬件平台间的数据交换。通过示例解析定长数据打包、字节对齐机制对内存占用的影响,以及变长字符串的处理策略,帮助开发者掌握二进制数据序列化的核心技巧。

不同编程语言对数据类型的定义存在差异,例如Go语言区分int32、int64、uint32、uint64等类型,各类型占用字节数不同。而在Python中,int类型既可表示有符号也可表示无符号整数,处理方式与Go等其他语言截然不同。这种差异导致在处理相同大小的数值时,Python与Go等语言的存储方式存在区别。

除了语言层面的差异,不同计算机硬件在数据存储方式上也有显著区别。例如,某些硬件中32 bit被视为一个word,而另一些硬件中64 bit才是一个word,且它们在数据存储的具体实现上各有不同。

当不同语言或不同机器之间需要通过network进行数据交换时,必须对发送和接收的字节流数据进行pack(打包)和unpack(解包)操作,以确保数据能够被正确解析和存储。

计算机如何存储整型

可以将计算机内存视为一个巨大的字节数组,每个字节包含8 bit信息,可表示0-255的无符号整型或-128至127的有符号整型。当存储大于8 bit的值时,该值通常被分割为多个8 bit的segment,存储在连续的内存空间中,每个segment占一个字节。部分处理器将高位数据存储在内存字节数组的头部,低位在尾部,这种机制称为big-endian(大端模式);另一些处理器则相反,低位在头部,高位在尾部,称为little-endian(小端模式)。

假设一个寄存器需要将0x12345678存储到内存地址1000处,big-endian和little-endian的存储表示分别如下

address big-endian little-endian
1000 0x12 0x78
1001 0x34 0x56
1002 0x56 0x34
1003 0x78 0x12

计算机如何存储character

与存储number类似,character通过特定的编码格式(如unicode)进行编码,然后以字节形式存储。

Python中的struct模块

Python提供了三个与pack和unpack相关的函数

struct.pack(fmt, v1, v2, ...)
struct.unpack(fmt, string)
struct.calcsize(fmt)

第一个函数pack负责将不同的变量打包在一起,生成一个字节字符串。

第二个函数unpack将字节字符串解包还原为变量。

第三个函数calcsize计算按照格式fmt打包后的结果所占用的字节数。

pack操作

Pack操作必须接受一个template string(模板字符串)以及一组需要打包的数据,这意味着pack处理的是定长数据。

import struct

a = struct.pack("2I3sI", 12, 34, "abc", 56)
b = struct.unpack("2I3sI", a)

print b

上述代码将两个整数12和34、一个字符串“abc”以及一个整数56打包成一个字节字符流,随后进行解包。其中打包格式明确指定了长度:“2I”表示起始是两个unsigned int,“3s”表示长度为4的字符串(注:原文此处描述可能有误,通常3s指3字节字符串,但示例中为“abc”即3字符,若按4字节算需补位,此处依原文描述保留),最后一个“I”表示最后紧跟一个unsigned int。因此,打印b的输出结果为:(12, 34, ‘abc', 56)。完整的Python pack操作支持的数据类型见下表。

Format C Type Python type Standard size Notes
x pad byte no value
c char string of length 1 1
b signed char integer 1 (3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
f float float 4 (4)
d double float 8 (4)
s char[] string
p char[] string
P void * integer (5), (3)

计算字节大小

可以利用calcsize来计算模式“2I3sI”占用的字节数

print struct.calcsize("2I3sI") # 16

可以看到,上述三个整型加一个3字符的字符串一共占用了16个字节。为什么是16个字节而不是15个字节?1个int占4字节,3个字符占3字节,理论总和应为15字节。但在struct的打包过程中,根据特定类型的要求,必须进行字节对齐(关于字节对齐详见 https://en.wikipedia.org/wiki/Data_structure_alignment)。由于默认unsigned int型占用四个字节,因此需要在字符串位置进行4字节对齐,导致即使是3个字符的字符串也要占用4个字节。

再看一下不需要字节对齐的模式

print struct.calcsize("2Is") # 9

由于单字符出现在两个整型之后,不需要进行额外的字节对齐,所以输出结果是9。

unpack操作

对于unpack而言,只要fmt对应的字节数与字节字符串string的字节数一致,就可以成功解析,否则unpack函数将抛出异常。例如,我们也可以使用如下的fmt解析出a:

c = struct.unpack("2I2sI", a)
print struct.calcsize("2I2sI")
print c  # 16 (12, 34, 'ab', 56)

不定长数据pack

如果打包的数据长度未知,该如何打包?这种情况在网络传输中非常常见。处理这种不定长内容的主要思路是将长度和内容一起打包,解包时首先解析内容的长度,然后再读取正文。

打包变长字符串

对于变长字符,在处理时可以把字符的长度作为数据内容的一部分一起打包。

s = bytes(s)
data = struct.pack("I%ds" % (len(s),), len(s), s)

上述代码把字符s的长度打包成内容,可以在进行内容读取时直接获取。

解包变长字符串

int_size = struct.calcsize("I")
(i,), data = struct.unpack("I", data[:int_size]), data[int_size:]

解包变长字符时,首先解包内容的长度,再根据内容的长度解包数据

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。