Python标准库—struct字节流处理

 2016年12月31日 19:56   Nick王   开发    0 评论   522 浏览 

Python使用struct模块执行Python值和C结构体之间的转换,从而形成Python字节(bytes)对象。

Python使用格式字符串作为底层C结构体的紧凑描述,进而根据这个格式字符串转换成Python值。

struct模块可以用来处理文件或者网络中的二进制数据。比如在用socket写网络通信的时候就要将字符串转换成二进制进行传输。

Python中没有二进制类型,但是可以使用string字符串类型来存储二进制数据,然后使用struct模块来对二进制数据进行处理。(Python3.5中有两个字节类: bytes和bytearrays)


主要函数

  • struct.pack(fmt, v1, v2, ...)   #根据给定的格式转换v1,v2返回字符串;str = struct.pack(fmt, v1,v2,...) 等同于 struct.pack_into(fmt, str, v1,v2,...)

  • struct.unpack(fmt, string)   #根据所给的fmt描述的格式将bytes反向解析出来,返回一个元组 res = struct.unpack(fmt, str) 等同于 struct.unpack_from(fmt, str, 0)

  • struct.calcsize(fmt)   #根据所给的fmt描述的格式返回该结构的大小


格式字符串

格式字符串的第一个字符:用于指示打包的字节的顺序,大小和对齐方式

字符
字节排序
大小和对齐
@
默认的,取决于本地系统,字节排序可根据sys.byteorder获取取决于本地系统使用C的sizeof获取
=取决于本地系统,根据sys.byteorder获取标准大小,按原字节数对齐
<小尾字节序标准大小,按原字节数对齐
>
大尾字节序
标准大小,按原字节数对齐
!
网络字节序(大尾)
标准大小,按原字节数对齐

LE(little-endian)和BE(big-endian)区别:

LE—最符合人的思维的字节序,地址低位存储值的低位,地址高位存储值的高位。

BE—最直观的字节序,地址低位存储值的高位,地址高位存储值的低位。

例如:双字0X01020304在内存中存储方式,LE=0403 02 01,BE=01 02 03 04。


格式化字符

格式字符
C类型Python类型标准大小
x填充字节无值1
c
char
长度为1的字符串1
b
signed char
整型1
B
unsigned char(无符号字符)整型
1
?
_Boolbool1
h
short整型2
H
unsigned short整型2
i
int整型4
I(大写i)unsigned int整型4
l(小写l)
long整型4
L
unsigned log整型4
q
long long整型8
Q
unsigned long long整型8
f
float浮点型float
4
d
double浮点型float
8
s
char[]
string

p
char[]string
P
void *整型


基本用法

>>> import struct
>>> struct.pack('=hhl', 1, 2, 3) #这里使用的是标准大小;
'\x01\x00\x02\x00\x03\x00\x00\x00'
>>> struct.unpack('=hhl', '\x01\x00\x02\x00\x03\x00\x00\x00')
(1, 2, 3)
>>> struct.calcsize('=hhl')
8
>>>

解包并赋值

>>> record = 'raymond   \x32\x12\x08\x01\x08'
>>> name, serialnum, school, gradelevel = struct.unpack('<10sHHb', record) #struct.unpack返回的是一个元组
>>> print name
raymond   
>>> print serialnum
4658
>>> print school
264
>>> print gradelevel
8

格式字符的排序可能对大小有影响,因为满足对齐要求所需的填充是不同的

>>> struct.pack('ci', 'a', 0x123)  #c的标准大小为1,i的标准大小为4
'a\x00\x00\x00#\x01\x00\x00'     #c有补0
>>> struct.pack('ic', 0x123, 'a')
'#\x01\x00\x00a'                #c没有补0
>>> struct.calcsize('ci')
8
>>> struct.calcsize('ic')
5
>>>

将整数值转换为二进制,读取二进制解包

>>> a = 0x01020304
>>> int(a)
16909060
>>> str = struct.pack('I', a)
>>> print str  #str是二进制,此处打印的是乱码

>>> repr(str)
"'\\x04\\x03\\x02\\x01'"
>>> b = struct.unpack('I', str)
>>> print b
(16909060,)
>>>

读取文件

>>> import struct
>>> fn = file('a.bmp', 'rb')
>>> s = fn.read(30)
>>> struct.unpack('<ccIIIIIIHH', s)
('B', 'M', 76854, 0, 54, 40, 160, 160, 1, 24)
>>> fn.close()

本例中读取的是BMP文件,BMP文件的文件头的结构按顺序如下:

  • 两个字节:'BM'表示Windows位图,'BA'表示OS/2位图;

  • 一个4字节整数:表示位图大小;

  • 一个4字节整数:保留位,始终为0;

  • 一个4字节整数:实际图像的偏移量;

  • 一个4字节整数:Header的字节数;

  • 一个4字节整数:图像宽度;

  • 一个4字节整数:图像高度;

  • 一个2字节整数:始终为1;

  • 一个2字节整数:颜色数。

所以这张图片的大小为76854字节, 规格为160*160的


注意:使用二进制打包数据的场景大部分都是对性能要求比较高的使用环境。而在上面提到的pack方法都是对输入数据进行操作后重新创建了一个内存空间用于返回,也就是说我们每次pack都会在内存中分配出相应的内存资源,这有时是一种很大的性能浪费。struct模块还提供了pack_into() 和 unpack_from()的方法用来解决这样的问题,也就是对一个已经提前分配好的buffer进行字节的填充,而不会每次都产生一个新对象对字节进行存储。




如无特殊说明,文章均为本站原创,转载请注明出处