python的struct模块

  了解c语言的人,一定会知道struct结构体在c语言中的作用,它定义了一种结构,里面包含不同类型的数据(int,char,bool等等),方便对某一结构对象进行处理。而在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,不必担心太多的问题,而当传递诸如int、char之类的基本数据的时候,就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。python中的struct模块就提供了这样的机制,该模块的主要作用就是对python基本类型值与用python字符串格式表示的C struct类型间的转化。再者, 有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体. struct模块中最重要的三个函数是pack(), unpack(), calcsize()


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

pack(fmt, v1, v2, ...) 

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

unpack(fmt, string)      

# 计算给定的格式(fmt)占用多少字节的内存 

calcsize(fmt)


struct中支持的format格式

093212_Vk5w_1458120.png

注1.q和Q只在机器支持64位操作时有意思

注2.每个格式前可以有一个数字,表示个数

注3.s格式表示一定长度的字符串,4s表示长度为4的字符串,但是p表示的是pascal字符串

注4.P用来转换一个指针,其长度和机器字长相关

注5.最后一个可以用来表示指针类型的,占4个字节

一个format字符串是用数字和上述定义的字符来表示的。如 5i 表示5个 int 型数,H 表示一个 unsigned short 类型的数, 0Q 则表示0个 unsigned long long 类型的数。在上面的表格中,如果表示了字符代表的位数,则这个字符前面再加数字,表示多个变量,如 3Q 表示3个变量;如果没有指明字符代表的位数,则前面加数据代表一个变量,如 3s 表示一个长度为3的字符串,在pack或者unpack的时候需要注意这一点。

struct支持的数据对齐方式

在与其它语言进行数据交互时,通常还要考虑的事情是:编译器是否有字节对齐?大端存储还是小端存储?在format字段串的第一个字符可以用来定义数据的对齐方式:

093408_N0vl_1458120.png

例如:

struct.unpack('>I', a)[0]  

format=">I"是什么意思,从google找了一下,有人说这个东西,可都是比较笼统,没能让我明白,于是硬着头皮看API:
By default, C numbers are represented in the machine’s native format and byte order, and properly aligned by skipping pad bytes if necessary (according to the rules used by the C compiler).
通常,C语言下数字都是机器语言的格式并且按照字节排序,同时在需要的情况下会利用跳过填补的字节来进行适当的调整

Alternatively, the first character of the format string can be used to indicate the byte order, size and alignment of the packed data。
非此即彼:字符串的第一个字符要么被用于表示字符串的字节的排序,或者是字符串的size,还有就是数据是否对准。

Native byte order is big-endian or little-endian, depending on the host system. For example, Motorola and Sun processors are big-endian; Intel and DEC processors are little-endian.
计算机的字节序要么是高位顺序,要么是低位的,这依赖于主机本身。比如,摩托罗拉和sun的处理器是高位的,但是intel和DEC的是低位的。

这样子就明白了上面的format=">I"的意思,也就是说按照高位顺序来格式化取得一个int或long值。下面问题就又来了,你怎么知道读取的就是一个int或long值呢?

通过看struct的文档,可以看到struct通过两张表制定了一定的format规则,我按照自己的观察,给他归纳为两类,一个是和C当中类型的对照,另一个就是选择按照高位还是低位来解释字节。上面已经说了高低字节顺序,那么观察和C对照的表格,发现I 代表的就是integer or long ,详细的可以去看python的API。

使用示例

示例1: C结构体打包和解包

一个C结构体:

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

现在接收到一个上述结果体数据,可以用unpack函数解析:

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

在上述代码中,H对应id,4s对应tag,2I对应version和count。!表示是由网络字节顺序传输。

这样,通过unpack,就解出对应的信息。同样,通过pack也可以将信息打包也对应的二进制格式:

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

pack函数将信息打包成一个字符串,实际上是类C结果体字节流,表示的就是一个Header结构体。

示例2: 格式化类型和二进制的转换
import struct 
a=12.34 
#将a变为二进制 
bytes=struct.pack('i',a)

pack后,bytes就是一个str字符串,内容与a的二进制存储内容相同

可以用如下代码进行反转换:

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

注意:unpack返回的是一个tuple。


# 使用struck.unpack获取子字符串 ,取前5个字符,跳过4个字符华,再取3个字符 
format = '5s 4x 3s'

print struct.unpack(format, 'Test astring') 
#('Test', 'ing') 

来个简单的例子吧,有一个字符串'He is not very happy',处理一下,把中间的not去掉,然后再输出。 
import struct 
theString = 'He is not very happy' 
format = '2s 1x 2s 5x 4s 1x 5s' 
print ' '.join(struct.unpack(format, theString)) 
输出结果: 
He is very happy


随后是关于网络字节的东东,从网上看来的,感觉有用:

Python的socket库采用string类型来发送和接收数据,这样当我们用
i = socket.recv(4)
来接收一个4字节的整数时,该整数实际上是以二进制的形式保存在字符串 i 的前4个字节中;大多数的时候我们需要的是一个真正的integer/long型,而不是一个用string型表示的整型。这时我们可以使用struct库:Interpret

strings as packed binary data. 对上面的情况,我们可以写
t = unpack("I", i)
第一个参数是格式化字符串,I指明字符串 i 包含的头一个数据项是一个以C语言的unsigned integer表示的整数,这里 i 只包含了一个数据项,实际上这个被解释的字符串也可以包含多个数据项,只要在格式化字符串里为每项数据指明一个格式即可;自然地,unpack返回的就是一个tuple类型了。




可能遇到的错误

1. struct.pack时可能会遇到:> struct.error: pack requires exactly 2 arguments

这一错误说明format中的参数个数与实际输入的参数个数不符。如 bytes = struct.pack('2I', a) 需要2个参数而只输入了一个

2. struct.unpack时可能会遇到:> struct.error: unpack requires a string argument of length *

这一错误说明format中参数代表的数据占用内存数,与输入的数据占用的内存长度不符。这时候可以用 struct.calcsize(fmt) 函数检查format字符代表的长度,及用len(string)函数来检测数据的长度。


转载于:https://my.oschina.net/u/1458120/blog/630744

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值