Python struct模块处理二进制数据详解

Felix
Felix
发布时间:2026-10-13 | 2人浏览过
原创

本文详解Python中struct模块处理二进制数据的方法,重点介绍pack、unpack和calcsize三个核心函数。通过示例展示如何将C结构体数据封装与解析,涵盖网络字节序、字节对齐及文件读写中的二进制模式注意事项,帮助开发者高效处理Socket通信和文件存储场景。

Python使用struct处理二进制数据的方法详解

在Python开发中,处理二进制数据(如文件存取、Socket通信)时,struct模块是常用工具。它允许Python程序与C语言中的结构体进行数据交互,实现二进制流的封装与解析。

  • struct模块的核心功能由三个主要函数提供:pack()、unpack()和calcsize()。
  • pack(fmt, v1, v2, ...)函数根据指定的格式字符串fmt,将多个参数封装为一个字节流字符串(行为类似C结构体)。
  • unpack(fmt, string)函数依据格式字符串fmt,对输入的字节流string进行解析,并返回包含解析结果的元组。
  • calcsize(fmt)函数用于计算指定格式fmt在内存中占用的字节数。

struct模块支持多种数据格式,具体对应关系如下表所示:

Format C Type Python 字节数
x pad byte no value 1
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsigned int integer or long 4
l long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 8
s char[] string 1
p char[] string 1
P void * long

注意事项: 1. q和Q格式仅在机器支持64位操作时有效。 2. 格式字符串前的数字表示该类型数据的个数。 3. s格式用于表示固定长度的字符串(如4s表示4字节字符串),而p格式用于表示Pascal字符串。 4. P格式用于转换指针,其长度取决于机器字长。 5. 另一个表示指针类型的格式占用4个字节。

为了与C/C++结构体进行数据交换,需考虑编译器的字节对齐问题。通常32位系统以4字节为单位对齐,struct模块会根据本地机器的字节顺序进行转换。可通过格式字符串的第一个字符调整对齐方式,定义如下:

Character Byte order Size and alignment
@ native native            凑够4个字节
= native standard        按原字节数
< little-endian standard        按原字节数
> big-endian standard       按原字节数
! network (= big-endian)

standard       按原字节数

使用方法是将对齐字符置于fmt字符串的开头,例如'@5s6sif'。

示例一:

假设存在如下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变量中即保存了解析出的信息。

同样,也可以方便地将本地数据打包为struct格式。

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

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

示例二:

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

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

接下来进行反向操作。

现有二进制数据bytes(本质为字符串),将其转换回Python数据类型:

a,=struct.unpack('i',bytes)

需注意,unpack返回的结果是元组(tuple)。

因此,如果只处理单个变量:

bytes=struct.pack('i',a)

解码时需采用以下方式:

a,=struct.unpack('i',bytes) 或者 (a,)=struct.unpack('i',bytes)

若直接使用a=struct.unpack('i',bytes),结果a将是一个元组(12.34,),而非原始的浮点数。

若数据由多个变量组成,可如下操作:

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表示两个整数。以下是可用的字符及其对应的Python类型:

注意:处理二进制文件时可能遇到的问题。

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

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

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

这与使用binfile=open(filepath,'r')打开文本文件有何区别?

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

第一,使用'r'模式时,若遇到'0x1A'字符,会被视为文件结束符(EOF)。而使用'rb'模式则无此限制。即,若用二进制写入后用文本读取,且文件中包含'0X1A',文本模式只会读出文件的一部分;使用'rb'则会读取到文件末尾。

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

如有疑问,欢迎留言或到本站社区交流讨论。感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

相关标签:

本站声明

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

Python 相关产品和应用

全部下载

专题列表