当前位置: 首页 > 后端技术 > Python

Python基础:struct和格式化字符

时间:2023-03-25 21:43:27 Python

介绍文件内容的存储方式有两种,一种是二进制,一种是文本。如果以文本的形式存储在文件中,那么从文件中读取时,会遇到Python中将文本转换为数据类型的问题。其实即使是以文本的形式存储,存储的数据也是有结构的,因为Python底层是用C写的,这里我们也称之为C结构。lib/struct.py就是负责这个结构转换的模块。对于struct中的方法,先看struct的定义:__all__=[#Functions'calcsize','pack','pack_into','unpack','unpack_from','iter_unpack',#Classes'Struct',#Exceptions'error']有6个方法和1个异常。下面主要看一下这六个方法的使用:方法名函数struct.pack(format,v1,v2,...)返回一个bytes对象,里面包含的值v1,v2,...按照packed格式字符串format的参数数量必须与格式字符串所需的值完全匹配。struct.pack_into(format,buffer,offset,v1,v2,...)将v1,v2,...按照格式字符串format进行打包,并将打包后的字节串写入从offset开始的可写缓冲buffer中。请注意,offset是必需的参数。struct.unpack(format,buffer)根据格式字符串格式从缓冲区buffer中解包(假设由pack(format,...)打包)。结果以元组形式返回,即使它只包含一项。缓冲区的字节大小必须与格式要求的大小相匹配。struct.unpack_from(format,/,buffer,offset=0)从位置offset开始按照格式字符串格式解包buffer。结果是一个元组,即使它只包含一个项目。struct.iter_unpack(format,buffer)根据格式字符串format从bufferbuffer中迭代解包。此函数返回一个迭代器,该迭代器将从缓冲区读取相同大小的块,直到其内容耗尽。struct.calcsize(format)返回与格式字符串格式对应的结构的大小(即pack(format,...)生成的字节串对象的大小)。这些方法主要是打包和拆包操作。其中一个非常重要的参数是format,它也被称为格式字符串,它指定了每个字符串被打包的格式。格式字符串格式字符串是用于在打包和解包数据时指定数据格式的机制。它们是使用指定正在打包/解包的数据类型的格式字符构造的。此外,还有一些特殊字符用于控制字节顺序、大小和对齐方式。字节顺序、大小和对齐默认情况下,C类型以机器的本机格式和字节顺序表示,并在必要时使用填充字节以进行正确对齐(根据C编译器使用的规则)。我们也可以手动指定格式字符串的字节顺序、大小和对齐方式:字符字节顺序大小对齐@byoriginalbytebyoriginalbytebyoriginalbyte=byoriginalbytestandardNoneBigEndianStandardNone!Network(=BigEndian)StandardNoneBigEndian和LittleEndian是两种存储数据的方式。第一种BigEndian将高位字节存储在起始地址。第二种LittleEndian将状态字节存储在起始地址。其实BigEndian更符合人类的读写习惯,而LittleEndian更符合机器的读写习惯。习惯。两大主流CPU阵营中,PowerPC系列采用bigendian存储数据,x86系列采用littleendian存储数据。如果不同的CPU架构直接通信,可能会因为读取顺序的不同而出现问题。填充仅在连续的结构成员之间自动添加。填充不会添加到编码结构的开头和结尾。使用非本机字节大小和对齐方式(即“<”、“>”、“=”和“!”)时不添加填充。格式字符让我们看看字符有哪些格式:formatCtypePythontypestandardsize(byte)xpaddingbytenoccharbytestringwithlength11bsignedcharinteger1Bunsignedcharinteger1?_Boolbool1hshortinteger2Hunsignedshortinteger2iintinteger4Iunsignedintinteger4llonginteger4Lunsignedlonginteger4qlonglonginteger8Qunsignedlonglonginteger8nssize_tintegerNsize_tintegere(6)floatingpointnumber2ffloatfloatingpointnumber4ddo??ublefloatingpointnumber8schar[]bytestringpchar[]bytestringPvoid*integerformatnumber比如我们要打包一个int对象,我们可以这样写:In[101]:fromstructimport*In[102]:pack('i',10)Out[102]:b'\n\x00\x00\x00'In[103]:unpack('i',b'\n\x00\x00\x00')Out[103]:(10,)In[105]:calcsize('i')Out[105]:4在上面的示例中,我们打包了一个int对象10,然后将其解包。并计算出i这种格式的长度是4个字节。可以看到输出结果为b'\n\x00\x00\x00'。这里我们不去深究这个输出的含义。开头的b表示字节,后面是字节编码。格式字符前面可以有整数重复计数。例如,格式字符串“4h”与“hhhh”的含义完全相同。查看如何打包4个short类型:In[106]:pack('4h',2,3,4,5)Out[106]:b'\x02\x00\x03\x00\x04\x00\x05\x00'In[107]:unpack('4h',b'\x02\x00\x03\x00\x04\x00\x05\x00')Out[107]:(2,3,4,5)formatbetween空白字符将被忽略,但如果是struct.calcsize方法,则格式字符中不能有空白字符。当以其中一种整数格式('b','B','h','H','i','I','l','L','q','Q')x,如果x超出格式的有效范围,将引发struct.error。除了数字之外,最常用的格式字符是字符和字符串。我们先来看看格式化字符的使用方法,因为字符的长度是1个字节,所以我们需要这样做:In[109]:pack('4c',b'a',b'b',b'c',b'd')Out[109]:b'abcd'In[110]:unpack('4c',b'abcd')Out[110]:(b'a',b'b',b'c',b'd')In[111]:calcsize('4c')Out[111]:4个字符前面的b表示这是一个字符,否则会被当作字符串处理。格式化字符串再看一下字符串的格式:In[114]:pack('4s',b'abcd')Out[114]:b'abcd'In[115]:unpack('4s',b'abcd')Out[115]:(b'abcd',)In[116]:calcsize('4s')Out[116]:4In[117]:calcsize('s')Out[117]:1可以看出对于字符串,calcsize返回以字节为单位的长度。填充的影响格式字符的顺序可能会影响大小,因为满足对齐要求所需的填充是不同的:>>>pack('ci',b'*',0x12131415)b'*\x00\x00\x00\x12\x13\x14\x15'>>>pack('ic',0x12131415,b'*')b'\x12\x13\x14\x15*'>>>calcsize('ci')8>>>calcsize('ic')5下面的例子我们将展示如何手动影响填充效果:In[120]:pack('llh',1,2,3)Out[120]:b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00'在上面的例子中,我们打包了三个数字1,2,3,只是格式不一样,分别是long,long,short。因为long是4字节,short是2字节,本质上是错位的。如果我们要对齐,可以在最后加0l代表0长,用于手动填充:In[118]:pack('llh0l',1,2,3)Out[118]:b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'在[122]中:解压('llh0l',b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00')Out[122]:(1,2,3)复杂的应用最后我们来看一个复杂的应用。在这个应用程序中,直接从unpack中读取的数据被读入元组:>>>record=b'raymond\x32\x12\x08\x01\x08'>>>name,serialnum,school,gradelevel=unpack('<10sHHb',record)>>>fromcollectionsimportnamedtuple>>>Student=namedtuple('Student','nameserialnumschoolgradelevel')>>>Student._make(unpack('<10sHHb',record))Student(name=b'raymond',serialnum=4658,school=264,gradelevel=8)这篇文章已经收录在http://www.flydean.com/13-python-struct-format-char/最流行的解释,最深奥的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!