Python struct模块处理二进制数据详解
本文详解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'表示换行。
如有疑问,欢迎留言或到本站社区交流讨论。感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













