Python基础之:struct和格式化字符
⽬录
简介
⽂件的存储内容有两种⽅式,⼀种是⼆进制,⼀种是⽂本的形式。如果是以⽂本的形式存储在⽂件中,那么从⽂件中读取的时候就会遇到⼀个将⽂本转换为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个异常。
我们主要来看这6个⽅法的使⽤:
⽅法名作⽤
struct.pack(format, v1, v2, ...)返回⼀个 bytes 对象,其中包含根据格式字符串format打包的值v1, v2, ... 参数个数必须与格式字符串所要求的值完全匹配。
struct.pack_into(format, buffer, offset, v1, v2, ...)根据格式字符串format打包v1, v2, ... 并将打包的字节串从offset开始的位置写⼊可写缓冲区buffer。请注意offset是必需的参数。
struct.unpack(format, buffer)根据格式字符串format从缓冲区buffer解包(假定是由pack(format, ...)打包)。返回的结果为⼀个元组,即使其只包含⼀个条⽬。缓冲区的字节⼤⼩必须匹配格式所要求的⼤⼩。
struct.unpack_from(format, /,
buffer, offset=0)从位置 offset 开始对 buffer 根据格式字符串 format 进⾏解包。结果为⼀个元组,即使其中只包含⼀个条⽬。
struct.iter_unpack(format, buffer)根据格式字符串format以迭代⽅式从缓冲区buffer解包。此函数返回⼀个迭代器,它将从缓冲区读取相同⼤⼩的块直⾄其内容全部耗尽。
struct.calcsize(format)返回与格式字符串format相对应的结构的⼤⼩(亦即pack(format, ...)所产⽣的字节串对象的⼤⼩)。
这些⽅法主要就是打包和解包的操作,其中⼀个⾮常重要的参数就是format,也被成为格式字符串,它指定了每个字符串被打包的格式。格式字符串
格式字符串是⽤来在打包和解包数据时指定数据格式的机制。它们使⽤指定被打包/解包数据类型的格式字符进⾏构建。此外,还有⼀些特殊字符⽤来控制字节顺序,⼤⼩和对齐⽅式。
字节顺序,⼤⼩和对齐⽅式
默认情况下,C类型以机器的本机格式和字节顺序表⽰,并在必要时通过填充字节进⾏正确对齐(根据C编译器使⽤的规则)。
我们也可以⼿动指定格式字符串的字节顺序,⼤⼩和对齐⽅式:
字符字节顺序⼤⼩对齐⽅式
@按原字节按原字节按原字节
=按原字节标准⽆
<⼩端标准⽆
>⼤端标准⽆
python怎么读取py文件!⽹络(=⼤端)标准⽆
⼤端和⼩端是两种数据存储⽅式。
第⼀种Big Endian将⾼位的字节存储在起始地址
第⼆种Little Endian将地位的字节存储在起始地址
其实Big Endian更加符合⼈类的读写习惯,⽽Little Endian更加符合机器的读写习惯。
⽬前主流的两⼤CPU阵营中,PowerPC系列采⽤big endian⽅式存储数据,⽽x86系列则采⽤little endian⽅式存储数据。如果不同的CPU架构直接进⾏通信,就由可能因为读取顺序的不同⽽产⽣问题。
填充只会在连续结构成员之间⾃动添加。填充不会添加到已编码结构的开头和末尾。
当使⽤⾮原字节⼤⼩和对齐⽅式即 '<', '>', '=', and '!' 时不会添加任何填充。
格式字符
我们来看下字符都有哪些格式:
格式C 类型Python 类型标准⼤⼩(字节)
x填充字节⽆
c char长度为 1 的字节串1
b signed char整数1
B unsigned char整数1
?_Bool bool1
格式C 类型Python 类型标准⼤⼩(字节)
h short整数2
H unsigned short整数2
i int整数4
I unsigned int整数4
l long整数4
L unsigned long整数4
q long long整数8
Q unsigned long long整数8
n ssize_t整数
N size_t整数
e(6)浮点数2
f float浮点数4
d double浮点数8
s char[]字节串
p char[]字节串
P void *整数
格式数字
举个例⼦,⽐如我们要打包⼀个int对象,我们可以这样写:
In [101]: from struct import *
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表⽰的是byte,后⾯是byte的编码。格式字符之前可以带有整数重复计数。例如,格式字符串'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)
格式之间的空⽩字符会被忽略,但如果是struct.calcsize ⽅法的话格式字符中不可有空⽩字符。
当使⽤某⼀种整数格式 ('b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q') 打包值 x 时,如果 x 在该格式的有效范围之外则将引发 。
格式字符
除了数字之外,最常⽤的就是字符和字符串了。
我们先看下怎么使⽤格式字符,因为字符的长度是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]: 4
In [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个long,从⽽进⾏⼿动填充:
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'
In [122]: unpack('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)
>>> from collections import namedtuple
>>> Student = namedtuple('Student', 'name serialnum school gradelevel')
>>> Student._make(unpack('<10sHHb', record))
Student(name=b'raymond ', serialnum=4658, school=264, gradelevel=8)
本⽂已收录于
最通俗的解读,最深刻的⼲货,最简洁的教程,众多你不知道的⼩技巧等你来发现!
欢迎关注我的:「程序那些事」,懂技术,更懂你!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论