Python:struct模块 2018/9/3
1.1.用途
执行Python值和表示为bytes对象C结构之间的转换,将Python基本类型转换
成byte.
基本类型包括:str,int,float,trube,list,dict;
用于处理文件或网络中的二进制数据以及其他来源;平台进行交互
python中b'str'表示字节,字节数组=二进制str。C语言用struct、union处
理字节,以及字节和int,float的转换。
注意
要处理与平台无关数据格式或省略隐式填充字节,请用standard大小和对齐而不
是native大小和对齐。
函数缓冲区参数:
缓冲区是指实现缓冲区协议并提供可读写缓冲区对象。常见bytes和bytearray
支持缓冲区协议的对象可被读取/填充无需额外的复制bytes对象。
python数据类型直接转换:
字符串: 编码仅能用于str,其他5中基本类型不能应用
a1=‘str’
b1=a1.encode(‘utf-8’) #b’str’
s1=b1.decode(‘utf-8’) #‘str’
数字:float不能转换
n = 10240099
b1 = (n & 0xff000000) >> 24
b2 = (n & 0xff0000) >> 16
b3 = (n & 0xff00) >> 8
b4 = n & 0xff
bs = bytes([b1, b2, b3, b4])
bs #b'\x00\x9c@c'非常麻烦
2.struct 函数
2.1、struct.unpack(fmt,buffer )把任意数据类型变成bytes;返回bytes
将Python值根据格式符,转换为字符串(字节流,或字节数组)
参数:
fmt是格式字符串;v1, v2, 表示要转换的python值。
2.2、struct.unpack(fmt, string)将bytes转换成python数据类型;返回一个元组。
根据格式字符串fmt从缓冲区缓冲区解压缩(可能打包)。结果是元组,即使它
只包含一个项目。
缓冲区大小(字节)必须与格式所需的大小相匹配,反映如下.calcsize()
*******************************************************************
打包/解包示例:
from struct import *
pack('>I', 10240099) #b'\x00\x9c@c'
#参数'>I':字节顺序是big-endian,就是网络序,I表示4字节无符号整数。
后面的参数个数要和处理指令一致。
pack('hhl', 1, 2, 3)# b'\x00\x01\x00\x02\x00\x00\x00\x03'
unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')# (1, 2, 3)
calcsize('hhl')# 8
unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')#(4042322160, 32896)
# 后面的bytes依次变为I:4字节无符号整数和H:2字节无符号整数。
a = 20;b = 400
s = pack('ii', a, b) #整数转换为字符串(字节流)
print(s, type(s))
输出:b’\x14\x00\x00\x00\x90\x01\x00\x00’ <class ‘bytes’>
print('length: ', len(s)) # 输出:length: 8
s2 = unpack('ii', s)
print(s2) # 输出:(20, 400)
# 格式符"i"表示转换为int,'ii'表示有两个int变量。
# 进行转换后的结果长度为8个字节(int类型占用4个字节)
2.3.struct.iter_unpack(fmt,buffer )
根据格式字符串fmt从缓冲区缓冲区解压缩。
返回一个迭代器,它将从缓冲区中读取大小相同块直到其所有内容都被消耗完。
缓冲区大小(字节)必须是格式所需大小的倍数,如所反映的。calcsize()
每次迭代产生一个由格式字符串指定的元组。
2.4.struct.calcsize(fmt ) 计算格式字符串所对应的结果的长度
返回与格式字符串fmt相对应的结构体大小(以及由此生成字节对象的大小 )
pack(fmt,…)
print("len: ", struct.calcsize('i')) # len: 4 字节
print("len: ", struct.calcsize('ff')) # len: 8
print("len: ", struct.calcsize('s')) # len: 1
print("len: ", struct.calcsize('d')) # len: 8
2.5.struct.pack_into(fmt,buffer,offset,v1,v2,... )
根据格式字符串fmt打包值v1,v2,... 并将打包字节写入可写缓冲区缓冲区,
从位置偏移开始。
请注意,偏移量是必需的参数。
2.6.struct.unpack_from(fmt,buffer,offset = 0 )
根据格式字符串fmt,从位置偏移量开始缓冲区中解压缩。结果是一个元组,
即使包含一个项目。
缓冲区大小(字节)减去偏移量必须至少为该格式所需大小,如calcsize()
import sys
from struct import *
from ctypes import create_string_buffer
buf = create_string_buffer(12)
print(repr(buf.raw))
#’\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00’
pack_into("iii", buf, 0, 1, 2, -1)
print(repr(buf.raw))
b’\x01\x00\x00\x00\x02\x00\x00\x00\xff\xff\xff\xff’
print(unpack_from("iii", buf, 0) ) # (1, 2, -1)
3.格式字符串
格式字符串是在打包和解包数据时用于指定预期布局的机制。它们由格式字符构成,它指定了打包/解压缩数据的类型。另外,还有用于控制字节顺序,大小和对齐的特殊字符。
3.1.字节顺序,大小和对齐
默认情况下,C类型以机器的本机格式和字节顺序表示,并在必要时通过跳
过填充字节(根据C编译器使用的规则)进行适当对齐。或者根据下表,格式
字符串第一个字符可用于指示打包数据字节顺序,大小和对齐方式:
Character Byte order Size Alignment
@ native native native
= native standard none
< little-endian standard none
> big-endian standard none
! network(=big-endian) standard none
假设第一个字符不是其中之一'@'。
本地字节顺序是大端或小端,取决于主机系统。
Intel x86和AMD64(x86-64)是小端;
摩托罗拉68000和PowerPC G5是高端的;
ARM和Intel Itanium具有可切换的字节序(双字节序)。
使用sys.byteorder来检查你的系统的字节序。
原生大小和对齐是使用C编译器sizeof表达式确定总是与本地字节顺序相结合。
区别'@'和'=':都使用本地字节顺序,但后者的大小和排列是标准化的。
表单'!'适用于那些声称自己不记得网络字节顺序是大端还是小端的穷人。
没有办法指示非本地字节顺序(强制字节交换); 使用'<'或的适当选择'>'。
笔记:
填充只会自动添加在连续结构成员之间。编码结构开头或结尾不添加填充。
使用非原生大小和对齐时,例如'<','>','='和'!'不会添加填充。
将结构的末尾与特定类型的对齐要求对齐,请使用重复计数为零的该类型的
代码结束格式。
3.2.格式字符
考虑到它们的类型,C和Python值之间的转换应该是显而易见的。
“标准大小”栏是指使用标准大小时打包值的大小(以字节为单位); 也就是
说,格式字符串以“ ‘<’,’>’” ‘!‘或“” 之一开头’=’。使用原生尺寸时,打包值的大小取决于平台。
笔记:
'?'转换码对应于_Bool由C99定义的类型。如果此类型不可用,则使用a进行模拟char。在标准模式下,它总是由一个字节表示。
将’q’和’Q’只有在平台C编译器支持C转换代码在本地模式中可用,或者在Windows上, 。它们始终可用于标准模式。long long__int64
当尝试使用任何整数转换代码打包非整数时,如果非整数具有__index__()方法,则在打包之前调用该方法将参数转换为整数。
在版本3.2中进行了更改:index()对于非整数的使用方法在3.2中是新的。
的’n’和’N’转换码只适用于本机的大小(选择为默认或与’@'字节顺序字符)。对于标准大小,您可以使用适合您的应用程序的其他任何整数格式。
对于’f’,'d’和’e’转换码,填充表示使用IEEE 754 binary32,binary64或binary16格式(‘f’,'d’或’e’分别地),而不管由所述平台中使用的浮点格式的。
该’P’格式字符仅适用于本地字节顺序(选择为默认或与’@‘字节顺序字符)。字节顺序字符’='选择使用基于主机系统的小端或大端排序。结构模块不会将其解释为本地排序,因此该’P’格式不可用。
IEEE 754 binary16“半精度”型在2008年IEEE 754标准修订版中引入。它有一个符号位,一个5位指数和11位精度(显式存储10位),并且可以表示大约6.1e-05和6.5e+04 全精度之间的数字。C编译器没有广泛支持此类型:在典型的机器上,可以使用无符号短整型存储,但不能用于数学运算。有关更多信息,请参阅维基百科页面上的半精度浮点格式。
格式字符之前可以有一个整数重复计数。例如,格式字符串的’4h’含义与“完全相同” ‘hhhh’。
格式之间的空格字符被忽略; 计数及其格式不能包含空格。
对于’s’格式字符,计数被解释为字节的长度,而不是像其他格式字符一样的重复计数; 例如, '10s’意味着一个10字节的字符串,而’10c’意味着10个字符。如果没有给出计数,则默认为1.对于打包,字符串将被截断或填充为空字节,以使其合适。对于解包,结果字节对象总是具有指定的字节数。作为一种特殊情况,'0s’意味着一个单一的空字符串(同时 '0c’表示0个字符)。
当包装一个值x使用的整数格式(一个’b’, ‘B’,‘h’,‘H’,‘i’,‘I’,‘l’,‘L’, ‘q’,‘Q’),如果x是在有效范围之外为该格式然后struct.error上升。
在版本3.1中更改:在3.0中,某些整数格式包装超出范围的值并引发DeprecationWarning而不是struct.error。
的’p’格式字符编码“帕斯卡串”,意思是存储在一个很短的可变长度的字符串的固定数目的字节,由计数给出。存储的第一个字节是字符串的长度,或255,以较小者为准。字符串的字节在后面。如果传入的字符串 pack()太长(比计数减1更长),则仅count-1存储字符串的前导 字节。如果字符串小于 count-1,则填充空字节,以便使用所有字节中的精确计数字节。请注意,对于unpack()中,'p’格式字符占用count的字节,但返回的字符串不能包含超过255个字节。
对于’?'格式字符,返回值是True或 False。打包时,使用参数对象的真值。本地或标准bool表示中的0或1将被打包,并且True在解包时会有任何非零值。
4.类
该struct模块还定义了以下类型:
类struct.Struct(格式)
返回一个新Struct对象,它根据格式字符串格式写入和读取二进制数据。创建一
个Struct对象并调用它的方法比调用struct具有相同格式的函数更有效率,因为格式字符串只需编译一次。
编译结构对象支持以下方法和属性:
pack(v1,v2,... )
与该pack()函数相同,使用编译后的格式。(len(result)将等于size。)
pack_into(buffer,offset,v1,v2,... )
与该pack_into()函数相同,使用编译后的格式。
unpack(缓冲区)
与该unpack()函数相同,使用编译后的格式。缓冲区的大小以字节为单位必须相
等size。
unpack_from(buffer,offset = 0 )
与unpack_from()函数相同使用编译后格式。缓冲区大小(字节,减去偏移量)
必须至少为 size。
iter_unpack(缓冲区)¶
与iter_unpack()函数相同,使用编译后格式。缓冲区大小以字节为单位size。
format
用于构造此Struct对象的格式字符串。
size
计算出结构体大小(以及由此pack()方法生成的字节对象的大小)对应于format
5.例
所有的例子都假设了一个本地字节顺序,大小,并与一个big-endian机器对齐。
from struct import *
#5.1解包字段可以通过将它们分配给变量或通过将结果包装到指定的元组中来命名:
record = b'raymond \x32\x12\x08\x01\x08'
print(record) #b'raymond 2\x12\x08\x01\x08'
name, serialnum, school, gradelevel = unpack('<10sHHb', record)
print('a1=%s;a2=%s;a3=%s;a4=%s'%(name, serialnum, school, gradelevel))
# a1=b'raymond ';a2=4658;a3=264;a4=8
from collections import namedtuple
Student = namedtuple('Student', 'name serialnum school gradelevel')
print(Student) #<class '__main__.Student'>
a=Student._make(unpack('<10sHHb', record))
print(a)
# Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8)
'''
namedtuple._make 返回命名元组的新实例,用新值替换指定字段
Point = namedtuple('Point', ['x', 'y'])
t = [11, 22]
Point._make(t)#Point(x=11, y=22)
'''
***************************************************************
#5.2 格式字符排序可能会影响大小,因为满足对齐要求所需的填充是不同的:
a=pack('ci', b'*', 0x12131415)
print(a) #b'*\x00\x00\x00\x15\x14\x13\x12'
a=pack('ic', 0x12131415, b'*')
print(a) #b'\x15\x14\x13\x12*'
a=calcsize('ci')
print(a) #8
a=calcsize('ic')
print(a) #5
#以下格式'llh0l'在末尾指定两个填充字节,假定长度在4个字节边界上对齐:
a=pack('llh0l', 1, 2, 3)
print(a) #b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'
***************************************************************
5.3.
import struct
string = b'test astring'
format = '5s 4x 3s'
print(struct.unpack(format, string)) # (b'test ', b'ing')
string = b'he is not very happy'
format = b'2s 1x 2s 5x 4s 1x 5s'
print(struct.unpack(format, string))
(b’he’, b’is’, b’very’, b’happy’)
***************************************************************
#5.4.Windows的位图文件(.bmp)
#BMP格式采用小端方式存储数据,文件头的结构按顺序如下:
#
#两个字节:'BM'表示Windows位图,'BA'表示OS/2位图;
#一个4字节整数:表示位图大小;
#一个4字节整数:保留位,始终为0;
#一个4字节整数:实际图像的偏移量;
#一个4字节整数:Header的字节数;
#一个4字节整数:图像宽度;
#一个4字节整数:图像高度;
#一个2字节整数:始终为1;
#一个2字节整数:颜色数。
#找一个bmp文件,没有话用“画图”画一个。
#读入前30个字节来分析:
s =b’\x42\x4d\x38\x8c\x0a\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00
\x00\x00\x80\x02\x00\x00\x68\x01\x00\x00\x01\x00\x18\x00’
#用unpack读取:
import struct
a=struct.unpack('<ccIIIIIIHH', s)
print(a)
#(b'B', b'M', 691256, 0, 54, 40, 640, 360, 1, 24)
#结果显示,b'B'、b'M'说明是Windows位图,位图大小为640x360,颜色数24。