struct模块

struct模块提供了用于在字节字符串和Python原生数据类型之间的转换,可以用于处理存储在文件中或从网络连接中存储的二进制数据,以及其他数据源。

struct中的pack函数把任意数据类型变成bytes

  • pack(fmt,v1,v2...) ->string
    按照给定的格式fmt, 将数据转换为字节流,并将该字节流返回
  • pack_into(fmt,buffer,offset,v1,v2)->None
    按照给定的格式fmt,将数据转换成字节流,并将字节流写入以offset开始的buffer中( buffer为可写的缓冲区,可用array模块)
  • unpack(fmt,v1,v2)->tuple
    按照给定的格式fmt,解析字节流,并返回解析结果
  • pack_from(fmt,buffer,offset)->tuple
    按照给定的格式fmt,解析以offset开始的缓冲区,并返回解析结果
  • calcsize(fmt)->int
    计算给定的格式fmt占用多少字节的内存,注意对齐方式

打包和解包

struct 支持将数据pack(打包成字节串),并能从字节串中unpack(解包)出数据。

import struct
import binascii

values = (1,'ab'.encode("utf-8"),2.7)
s = struct.Struct("I 2s f") # 指定格式
packed_data = s.pack(*values)

print(" 原始值 : ", values)
print(" 格式符 : ", s.format)
print(" 占用字节 : ", s.size)
print(" 打包结果: ", binascii.hexlify(packed_data))
# binascii.hexlify()将打包的值转换为十六进制字节序列显示出来

格式符中的空格用于分隔各个指示器(indicators), 在编译格式时会被忽略

import struct
values = (1,'ab'.encode("utf-8"),2.7)
s = struct.Struct("I 2s f")
packed_data = s.pack(*values)

unpacked_data = s.unpack(packed_data)
print(unpacked_data)
# 没有分隔符
# 2个整数,1个有3个字符的字符串,一个整数
a = struct.pack("2I3sI", 12, 34, b"abc", 56)
b = struct.unpack("2I3sI", a)
print('a:',a)
print('b:',b)

字节顺序和对齐

默认情况下,pack使用本地C库的字节顺序来编码的。格式化字符串的第一个字符可以用来表示填充数据的字节顺序、大小和对齐方式。

一般情况下,主机字节序小端模式,网络字节序大端模式

python struct模块_Python

如果格式符中没有设置这些,那么默认将使用 @。

print(struct.calcsize("2I3sI"))# 16

上面的三个整型加一个 3 字符的字符串一共占用了 16 个字节

1 个 int 4 字节,3 个字符 3 字节

在 struct的打包过程中,根据特定类型的要求,必须进行字节对齐

由于默认 unsigned int 型占用四个字节,因此要在字符串的位置进行4字节对齐,因此即使是 3 个字符的字符串也要占用 4 个字节。

不定长数据pack

处理不定长的内容的主要思想是把长度和内容一起打包,解包时先解析内容的长度,然后再读取正文

# 对于变长字符串在处理时把字符串的长度当成数据的内容一起打包
s = bytes(s)
data = struct.pack("I%ds"%(len(s),),len(s),s)
# 解包变长字符串
int_size = struct.calcsize("I")
i = struct.unpack("I",data[:int_size])
data = struct.unpack("%ds"%(i[0],),data[int_size:])
# 解包变长字符串时先解包内容的长度,再根据内容的长度解包数据

格式符

python struct模块_解包_02

缓冲区

可以通过避免为每个打包结构分配新缓冲区的开销来优化

pack_into()unpack_from()方法支持直接写入预先分配的缓冲区。

  • pack_into(format, buffer, offset, v1, v2, ...)
  • unpack_from(format, buffer, offset=0)

在组包拆包时,可以指定所需的偏移量,这让组包拆包变得更加灵活。

import ctypes
import struct

s = struct.Struct('I 2s f')
sbuf = struct.Struct('I 2s f')
values = (1, 'ab'.encode('utf-8'), 2.7)
s.pack_into(sbuf, 0, *values)

res = s.unpack_from(sbuf, 0)

参考

python(29):struct模块_python struct_python开发笔记的博客