struct模块

该模块在Python值和表示为Python bytes对象的C结缓冲区构体之间进行转换。可用于处理存储在文件中或者来自网络连接,以及其它来源的二进制数据。它使用格式化字符串,作为C结构体布局的简洁描述,以及从Python值,或者到Python值的预期转换。

注意:默认情况下,封装给定的C结构体的结构包括填充字节,以便为涉及的C类型保持正确的对齐;类似地,解包时会考虑对齐。选择此行为使得被封装的结构体的字节完全对应于C结构体在内存中的布局。要处理平台无关的数据格式,或者忽略隐式的填充字节,可以使用standard大小和对齐,代替native大小和对齐,查看字节顺序,大小和对齐了解详情。

有几个struct函数(和Struct的方法)接收一个buffer参数。它引用实现了Buffer协议的对象,并提供一个可读,或可读写的缓存。用于该目的的最常见的类型是bytesbytearray,但是可以视为字节数组的许多其它类型实现了Buffer协议,因此它们可以读和填充,而不需要额外的从bytes对象复制。

1 函数和异常

该模块定义了以下异常和函数:

1.1 异常 struct.error

在各种场合下抛出异常;参数是描述错误的字符串。

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

根据格式化字符串fmt封装,返回一个包括v1v2等值的字节对象。参数必须与格式化所需的值完全匹配。

1.3 struct.pack_into(ftm, buffer, offset, v1, v2, ...)

根据格式化字符串fmt,封装v1v2等值,并从位置offset开始,将封装的字节写入可写缓冲区buffer中。注意,offset是必需的参数。

1.4 struct.unpack(fmt, buffer)

根据格式化字符串fmt,从缓冲区buffer(假设由pack(fmt, ...)封装)中解包。即使结果只包含一项,也是一个元组。缓冲区的大小(以字节为单位)必需与格式所需的大小匹配,比如calcsize()所得的结果。

1.5 struct.unpack_from(fmt, buffer, offset=0)

根据格式化字符串fmt,从位置offset开始解包。即使结果只包含一项,也是一个元组。缓冲区的大小(以字节为单位)减去offset,至少是格式化所需的大小,比如calcsize()所得的结果。

1.5 struct.iter_unpack(fmt, buffer)

版本3.4中新增。

根据格式化字符串fmt,从缓冲区buffer中迭代解包。该函数返回一个iterator,它将从缓冲区中读取大小相等的块,直到所有内容被耗尽。缓冲区的大小(以字节为单位)必须是格式化所需大小的倍数,比如calcsize()所得的结果。

每次迭代yield一个由格式化字符串指定的元组。

1.6 struct.calcsize(fmt)

返回对应于格式化字符串fmt的结构体(以及由pack(fmt, ...)生成的字节对象)的大小。

2 格式化字符串

封装和解包数据时,格式化字符串是用于指定预期布局的机制。它们由Format Characters构成,指定封装和解包的数据类型。此外,还有一些特殊字符控制字节顺序,大小和对齐。

2.1 字节顺序,大小和对齐

默认情况下,C语言类型以机器的本机格式和字节顺序表示,如果需要(根据C编译器使用的规则),跳过填充字节,以便正确对齐。

或者根据下表,格式化字符串的第一个字符用于表明封装数据的字节顺序,大小和对齐:

字符 字节顺序 大小 对齐
@ 本机 本机 本机
= 本机 标准
< 小端 标准
> 大端 标准
! 网络(等于大端) 标准

如果第一个字符不是其中之一,则假定为“@”。

本地字节顺序是大端或小端,这取决于主机系统。例如,Intel x86和AMD64(x86-64)是小端字节序;摩托罗拉68000和PowerPC G5是大端字节序;ARM和Intel Itanium可切换字节顺序(双字节顺序)。使用sys.byteorder检查系统的字节顺序。

使用C编译器的sizeof表达式确定本机大小和对齐。这总是与本机字节顺序结合。

标准大小仅取决于格式化字符;参考格式化字符中的表格。

注意“@”和“=”之间的区别:都使用本机字节顺序;但后者的大小和对齐是标准的。

“!”形式用于忘记网络字节顺序是大端还是小端的情况。

没有办法表示非本地字节顺序(强制字节交换);使用适当的“<”或“>”。

注意:

  1. 填充仅在连续的结构体成员之间自动添加。在编码的结构体的开头或结尾不填充。
  2. 使用非本地大小和对齐时,不添加填充,比如“<”,“>”,“=”和“!”。
  3. 要将结构体结尾与特殊类型的对齐要求对齐,使用重复计数为0的该类型的代码结束格式。

2.2 格式化字符

版本3.1中修改:在3.0中,有些整数格式封装了超出范围的值,并抛出DeprecationWarning代替struct.error

版本3.2中修改:新增为非整数使用__index__()方法。

版本3.3中修改:添加“n”和“N”格式。

版本3.6中修改:添加“e”格式。

格式化字符有以下含义:C和Python值之间的转换应该明确指定类型。“标准大小”列表示,使用标准大小时,被封装值的字节大小;也就是格式化字符串以“<”,“>”,“!”或“=”开始时。当使用本机大小,被封装值的大小是平台相关的。

格式 C类型 Python类型 标准大小 提示
x 填充字节 没有值
c char 长度为1的字节 1
b signed char integer 1 1,3
B unsigned char integer 1 3
? _Bool bool 1 1
h short integer 2 3
H unsigned short integer 2 3
i int integer 4 3
I unsigned int integer 4 3
l long integer 4 3
L unsigned long integer 4 3
q long long integer 8 2,3
Q unsigned long long integer 8 2,3
n ssize_t integer 4
N size_t integet 4
e 7 float 2 5
f float float 4 5
d double float 8 5
s char[] bytes
p char[] bytes
P void * integer 6

提示:

  1. “?”转换码对应的_Bool类型由C99定义。如果该类型不可用,使用char模拟。在标准模式下,它总是由一个字节表示。
  2. 只有C编译器支持long long类型,或者Windows上的__int64时,“q”和“Q”转换码才在本机模式下可用。在标准模式下它们总是可用。
  3. 使用图任何整数转换码封装一个非整数时,如果非整数有__index__()方法,则在封装前会调用该方法把参数转换为整数。
  4. “n”和“N”转换只在本机大小中可用(选择默认或“@”字节顺序)。对于标准大小,可用使用适合应用程序的其它整数格式。
  5. 对于“f”,“d”和“e”转换码,封装分别表示使用IEEE 754的binary32,binary64或binary16格式,而不管平台使用的浮点数格式。
  6. “P”只在本机字节顺序可用(选择默认或“@”字节顺序)。“=”根据主机系统选择使用小端或大端顺序。struct模块不会解释为本机顺序,因此“P”格式不可用。
  7. IEEE 754标准的2008版本中,引入了IEEE 754 binary16半精度。它有一个符号位,5位指数和11位精度(其中10位存储),全精度可用表示大约6.1e-056.1e-05。C编译器没有广泛支持该类型;在典型的机器中,unsigned short可用于存储,但不适合数据运算。

可以在格式化字符之前使用整数表示重复次数。例如,“4h”完全等价于“hhhh”。

尽管次数和它的格式不可能包含空格,还是会忽略格式化字符之前的空格。

对于“s”格式化字符,次数表示字节的长度,而不是其它格式化字符的重复次数;例如,“10s”表示单个10个字节的字符串,“10c”表示10个字符。如果没有指定次数,默认为1。对于封装,字符串被截断,或者用空字节填充,让其正确适应。对于解包,生成的字节对象总是指定的字节数。对于特殊情况,“0s”表示单个空字符串(“0c”表示0个字符)。

使用其中一个整数格式(“b”,“B”,“h”,“H”,“i”,“I”,“l”,“L”,“q”,“Q”)封装值x的时候,如果x超出格式的有效返回,会抛出struct.error异常。

格式化字符“p”编码一个Pascal string,表示一个short可变长度的字符串存储在固定字节数中,其长度由次数指定。第一个字节存储字符串的长度,或者255,取两者中的较小者。接着是字节的字符串。如果传递到pack()中的字符串太长(长于次数减1),则只存储前count-1个字节。如果字符串比count-1短,则使用空字节填充,以便使用所有字节。注意,对应unpack(),“p”格式化字符消耗count个字节,但返回的字符串永远不会超过255个字节。

对于“?”格式化字符,返回值是TrueFalse。封装时,使用参数对象的真值。在本机或标准bool表示中,0或1会被封装,解包时任何非0值是True

2.3 示例

注意:所有示例假设本机字节顺序和大小,以及大端机器对齐。

封装/解包三个整数的基本示例:

>>> from struct import *
>>> 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

解包的字段可以通过分配给变量,或者将结果包装在命名元组中来命名:

>>> 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)

格式化字符的顺序可能会对大小有影响,因为满足对齐需求的填充是不同的:

>>> 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

下面的“11h01”格式,在结尾指定了两个填充字节,假设long在4字节边界对齐:

>>> pack('11h01', 1, 2, 3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03\x00\x00'

这只在本机大小和对齐有效时才其作用;标准大小和对齐不强制任何对齐。

3 类

struct模块还定义了以下类型:

3.1 类struct.Struct(format)

返回一个新的Struct对象,更具格式化字符串format写入和读取二进制数据。创建一个Struct对象,并调用它的方法,比用同样格式调用struct函数更高效,因为格式化字符串只需要编译一次。

编译的Struct对象支持以下方法和属性:

3.1.1 pack(v1, v2, ...)

完全等同于pack()函数,使用编译后的格式。(len(result)将会等于size

3.1.2 pack_into(buffer, offset, v1, v2, ...)

完全等同于pack_into()函数,使用编译后的格式。

3.1.3 unpack(buffer)

完全等同于unpack()函数,使用编译后的格式。缓冲区的大小(以字节为单位)必须等于size

3.1.4 unpack_from(buffer, offset=0)

完全等同于unpack_from()函数,使用编译后的格式。缓冲区的大小减去offset(以字节为单位)必须至少为size

3.1.5 iter_unpack(buffer)

版本3.4中新增。

完全等同于iter_unpack()函数,使用编译后的格式。缓冲区的大小(以字节为单位)必须是size的倍数。

3.1.6 format

构造该Struct对象的格式化字符串。

3.1.7 size

对应于format的结构体(以及由pack(fmt, ...)生成的字节对象)的大小。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容