Python struct模块详解:二进制数据打包与解包实战

Ryan
Ryan
发布时间:2026-10-14 | 5人浏览过
原创

本文深入解析Python中struct模块的核心用法,详细讲解pack()与unpack()函数如何高效处理二进制数据。通过Socket通信示例演示结构体打包,并重点剖析二进制文件读写时文本模式与二进制模式的差异,特别是换行符转换与EOF处理问题,帮助开发者避免常见的二进制文件读取错误。

Python struct模块概述

在Python开发中,处理二进制数据是常见需求,例如文件存储或Socket网络通信。此时,Python内置的struct模块提供了强大的支持,它能够模拟C语言中的结构体操作,方便地处理二进制流。

struct模块主要包含三个核心函数:pack()用于将数据打包,unpack()用于解包数据,以及calcsize()用于计算格式字符串占用的字节数。


# 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
pack(fmt, v1, v2, ...) 
 
# 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple
unpack(fmt, string)  
 
# 计算给定的格式(fmt)占用多少字节的内存
calcsize(fmt)

Format格式字符串详解

在format字符串(fmt)中,支持多种数据格式,具体对应关系如下表所示:

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)

使用这些格式时,需将其置于fmt字符串的首位,例如组合格式'@5s6sif'。

Socket通信中的结构体处理

以下是具体的应用示例1:

假设定义如下C语言结构体:


struct Header
{
 unsigned short id;
 char[4] tag;
 unsigned int version;
 unsigned int count;
}

当通过socket.recv()接收到上述结构体的二进制数据并存储在字符串s中时,可以使用unpack()函数进行解析:


import struct
id, tag, version, count = struct.unpack("!H4s2I", s)

在格式字符串中,'!'代表使用网络字节序进行解析,这是因为网络传输的数据通常遵循网络字节序。随后的'H'代表一个无符号短整型(unsigned short)的id,'4s'代表长度为4字节的字符串,'2I'则表示两个无符号整型(unsigned int)数据。

通过一次unpack调用,id、tag、version和count变量即可获取到对应的信息值。

同样地,也可以方便地将本地数据重新pack为struct格式:


ss = struct.pack("!H4s2I", id, tag, version, count);

pack函数将id、tag、version、count按照指定格式转换为结构体Header,生成的ss是一个字符串(本质上是类似C结构体的字节流),可以通过socket.send(ss)将其发送出去。

数值类型的打包与解包示例

示例2演示了数值类型的打包与解包:


import struct
 
a=12.34

# 将a变为二进制
bytes=struct.pack('i',a)

此时,bytes变量是一个字符串对象,其字节内容与变量a的二进制存储内容完全一致。

进行反向操作时,若已有二进制数据bytes(即字符串),可以将其转换回Python数据类型:


# 注意,unpack返回的是tuple !!
a,=struct.unpack('i',bytes)

如果涉及多个数据的组合,处理方式如下:


a='hello'
b='world!'
c=2
d=45.123

bytes=struct.pack('5s6sif',a,b,c,d) 此时的bytes即为二进制形式的数据,可以直接写入文件,例如执行binfile.write(bytes)。

随后,当需要读取数据时,可以执行bytes=binfile.read()。

接着通过struct.unpack()将读取的数据解码为Python变量:


a,b,c,d=struct.unpack('5s6sif',bytes)

'5s6sif'被称为fmt(格式化字符串),由数字和字符组成。其中'5s'表示占用5个字符的字符串,'2i'表示2个整数等。以下是可用的字符及其对应的Python类型(ctype):

二进制文件读写注意事项

在处理二进制文件时,需要注意一些关键问题。

处理二进制文件时,应采用如下方式打开文件:


binfile=open(filepath,'rb')  #读二进制文件
 
binfile=open(filepath,'wb') #写二进制文件

那么,使用'rb'或'wb'与使用'r'或'w'打开文件(binfile=open(filepath,'r'))究竟有何区别?

主要区别体现在两个方面:

第一,使用'r'模式时,如果文件中出现'0x1A'字符,系统会将其视为文件结束符(EOF)。而使用'rb'模式则不存在此问题,即如果用二进制写入再用文本模式读取,且文件中包含'0X1A',则只能读出文件的一部分。使用'rb'模式会一直读取到文件末尾。

第二,对于字符串x='abc\ndef',使用len(x)得到的长度为7,其中\n是换行符,实际值为'0X0A'。当使用'w'(文本模式)写入时,在Windows平台上会自动将'0X0A'转换为两个字符'0X0D'和'0X0A',导致文件长度变为8。当使用'r'(文本模式)读取时,又会将其自动转换回原来的换行符。如果改用'wb'(二进制模式)写入,则保持一个字符不变,读取时也是原样读取。因此,如果用文本模式写入而用二进制模式读取,就需要考虑这多出的一个字节('0X0D',即回车符)。在Linux环境下不会发生这种转换,因为Linux仅使用'0X0A'表示换行。

以上就是Python使用struct处理二进制(pack和unpack用法)的详细内容,更多关于python struct处理二进制的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表