python使用struct处理二进制数据(使用pack和unpack进行打包和解包)

前言:

背景:很多时候我们需要用python处理二进制数据。例如,存储文件、进行socket操作等。这个时候就需要用到struct模块。

struct用途:

(1)按照指定格式将Python数据转换为字符串(字节流)。如网络传输时不能直接传输int/long数据,此时要先将int/long转化为字节流,然后再发送;
(2)按照指定格式将字节流转换为Python指定的数据类型;
(3)处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;
(4)处理C或者C++语言中的结构体;

接下来我们介绍struct模块中最重要的pack,unpack两个函数。

注:例如二进制流与数值的互相转换在编解码、开发小工具方面用途非常广泛。

二、struct模块

1、pack()函数

#按照给定的格式(format),把数据封装成字符串(实际上是类似于C结构体的字节流)
pack(format,v1,v2,...)

2、unpack()函数

#按照给定的格式(format),解析字节流string,返回解析出来的tuple
pack(format,string)

3、calcsize()函数

#计算给定的格式(format)占用多少字节的内存。
#注:icc表示三个成员数据类型是interger, char, char。
size = struct.calcsize(’@icc’) 结果是6。
size = struct.calcsize(’@cic’) 结果是9。

4、上述format支持的格式参见末尾表格。关于格式有如下几点说明:

(1)每个格式前面可以带一个数字,表示个数;

(2)s格式表示一定长度的字符串,4s表示长度为4的字符串;

(3)P用来转换一个指针,其长度和机器字长相关;

5、为了同C结构体交换数据还要考虑C或者C++编译器用到的字节对齐。字节序和对齐方式有如下5种:

CharacterByte orderSizeAlignment
@(默认)本机本机本机,凑够4字节
=本机标准none,按原字节数
<小端标准none,按原字节数
>大端标准none,按原字节数
!network(大端)标准none,按原字节数

(1)Byte order列中的native(本机)表示和当前系统的字节序一致;

(2)size列的native(本机)表示这个时候数据类型的大小和sizeof一致;如果是standard(标准)表示数据类型的长度是固定的;

(3)alignment是native(本机)的时候表示和当前系统的字节对齐方式一致。但是要注意的是python中的字节填充(padding)只会在连续的结构体成员之间添加,不会在结构体的开头和结尾添加。例如:

size = struct.calcsize(’@icc’) 结果是6。
size = struct.calcsize(’@cic’) 结果是9。

原因是系统字节对齐是4,第一个c和i之间添加了3个字节padding,但是最后一个c,后边没有添加padding,所以结果是9而不是12。

1)如何解决这个问题?当然有办法,在格式字符串的最后添加一个“0X”,“X”是一个数据类型,如“i”,代表的就是long,这时候就是指定数据类型,这时候就和C语言中的字节对齐完全一致。
size = size = struct.calcsize(’@cic0i’) 结果是12。

2)举个例子
在C代码中定义结构体:
Struct Person{
int fortune;
char height;
char weight;
};
假设从C代码中获取到的结构体数据是buf, 在python中使用struct模块按照上述的格式解析结构体中的数据。
python中使用struct.unpack(’@icc’, buf);解析结构体数据,这个时候就会报错。原因就是字节对齐导致两方的长度不一样。
size = struct.calcsize(’@icc’) 的结果是6,而sizeof(Person)的长度是8。这样就会导致解析错误。这是后需要指定格式对齐。
struct.unpack("@icc0i",buf)。这样就能正确解析C语言中的结构体。

(4)默认情况下使用字节序且对齐字符即表格中的”@“。

(5)对于涉及到socket收发的情况我们一般用”i“,即网络字节序。

三、使用举例

1、首先要明白不同类型数据的自然边界对齐值,如下:

数据类型

对齐值

char

1

short

2

int

4

long

4(8,64位)

float

4

double

8

void *

4(8,64位)

注意:如果一个结构体全部由char组成,也就是说其对齐值为1;此时完全没有担心字节对齐引发问题的必要了。

2、可以随心所欲的在C/C++的结构体之前来回解析与转换(也就是说socket发送C结构体其实一样很好操作)

 比如有如下一个结构体

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

通过socket.recv接收到这个结构体buf的时候需要要把它解析出来,此时就可以使用unpack函数。

import struct
#'!'表示我们用网络字节序解析,因为我们是从网络上接收到这个buf的。
#'H'表示unsigned short的id;
#'4s'表示四个字节长的字符串;
#'2I'表示有两个unsigned int类型的数据;
id,tag,version,count = struct.unpack('!H4s2I',buf)

同样我们也可以很方便的把本地数据在pack成struct格式。

buf = struct.pack('!H4s2I',id,tag,version,count)

pack函数就是把四个参数按照指定的格式转换成了结构体Header,buf现在是一个字符串(实际上类似于C结构体的字节流),这个字节流是可以通过socket.send(buf)发送出去的。接受者按照上面unpack的方法就可以还原出原始的四个参数。

3、举例二:

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个整数等等,下面是可用的字符及类型,ctype表示可以与python中的类型一一对应。

struct的format支持的格式:

FormatC TypePython typeStandard sizeNotes
xpad byteno value
ccharstring of length 11
bsigned charinteger1(3)
Bunsigned charinteger1(3)
?_Boolbool1(1)
hshortinteger2(3)
Hunsigned shortinteger2(3)
iintinteger4(3)
Iunsigned intinteger4(3)
llonginteger4(3)
Lunsigned longinteger4(3)
qlong longinteger8(2), (3)
Qunsigned long longinteger8(2), (3)
ffloatfloat4(4)
ddoublefloat8(4)
schar[]string1
pchar[]string
Pvoid *integer(5), (3)

Python 可以很方便地处理二进制数据。以下是一些处理二进制数据的常用函数和方法: 1. 内置函数 `open()`:可以用于打开二进制文件。 ```python f = open('file.bin', 'rb') # 注意使用二进制模式打开文件 ``` 2. 内置函数 `struct.pack()` 和 `struct.unpack()`:可以用于将二进制数据打包和解。 ```python import struct # 将整数 42 打包二进制数据 packed = struct.pack('i', 42) print(packed) # 输出 b'*\x00\x00\x00' # 将二进制数据为整数 unpacked = struct.unpack('i', packed) print(unpacked) # 输出 (42,) ``` 3. 内置模块 `array`:可以用于创建和操作数组。 ```python import array # 创建一个含整数的数组 a = array.array('i', [1, 2, 3]) print(a) # 输出 array('i', [1, 2, 3]) # 将数组写入文件 with open('data.bin', 'wb') as f: a.tofile(f) # 从文件中读取数组 with open('data.bin', 'rb') as f: b = array.array('i') b.fromfile(f, 3) print(b) # 输出 array('i', [1, 2, 3]) ``` 4. 内置模块 `bytes` 和 `bytearray`:可以用于创建和操作字节序列。 ```python # 创建一个字节序列 b = bytes([0x48, 0x65, 0x6c, 0x6c, 0x6f]) print(b) # 输出 b'Hello' # 将字节序列转换为可打印的字符串 s = b.decode('utf-8') print(s) # 输出 'Hello' # 创建一个可变字节序列 ba = bytearray(b) ba[0] = 0x68 # 修改第一个字节 print(ba) # 输出 bytearray(b'hello') ``` 以上只是一些常见的二进制数据处理函数和方法,Python 还有很多其他的工具和库可以用于处理二进制数据,如 `numpy`、`pandas` 等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

焱齿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值