[Python] Python struct模块学习

struct模块

struct模块用于将Python中的bytes类型对象和C语言中的struct进行转换,一般用于处理存储在文件或网络中的二进制文件,使用struct模块时需要指定一个格式规范,来对字节大小、顺序、对齐等进行约束。

常用方法:

struct.pack(fmt,v1,v2,…):以一个struct格式化字符串以及一个或多个值为参数,返回一个bytes对象,其中存放的是按照该格式规范表示的所有的这些参数值。
struct.unpack(fmt,buffer):以一个格式规范和一个bytes/bytearray对象为参数(缓冲区),返回一个元组,缓冲区的大小必须和格式规范fmt保持一致。
struct.calcsize(fmt):返回格式规范fmt的大小,和pack()打包后的bytes对象大小一致。

格式规范字符串

格式字符串是在打包和解包数据时用于指定预期布局的机制,它们由格式字符构成,它指定了打包/解压缩数据的类型。另外,还有用于控制字节顺序,大小和对齐的特殊字符。指定的一些值如下,下表来自于python官方文档,更多细节请参考Python官方文档struct模块

Format C Type Python type Standard size Notes
x pad byte no value
c char bytes of length 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
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long 8 (2), (3)
n ssize_t integer (4)
N size_t integer integer (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)

如:

# 从上表中看出,h在C中表示short,python中表示integer,占2个字节大小,因此返回的bytes对象中包含2*3个字节
>>> struct.pack("hhh",1,2,3) 
b'\x01\x00\x02\x00\x03\x00'
>>> 
# l在C中表示long型,在python中表示integer,占4个字节,因此返回的不要bytes对象中1,2占分别4个字节,3占2字节
>>> struct.pack("llh",1,2,3)
b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00'
>>> 

在上表中,c表示一个byte对象,s通常用于对str进行格式规范,10c表示10个byte对象,10s表示10个byte组成的str,如:

>>> struct.pack("4sl","just".encode("utf-8"),3)
b'just\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'
>>> 
>>> struct.pack("4cl","j".encode("utf8"),"j".encode("utf8"),"j".encode("utf8"),"j".encode("utf8"),3)
b'jjjj\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'
>>> 
字节顺序(ByteOrder)、大小(Size)和对齐(alignment)

默认情况下,C语言中的数据类型使用本地格式和顺序表示,并且在必要时会跳过填充字节保持对齐,因此,在将Python和C之间进行转换时,可以使用下表中的字符作为格式规范可以指示这三个属性:

Character Byte order Size Alignment
@ native native native
= native standard none
< little-endian standard none
> big-endian standard none
! network (= big-endian) standard

little-endian和big-endian称为字节序,其中广泛使用的是little-endian,如果格式规范中没有指定字节序,则使用平台本身字节序。如:

>>> struct.pack(",1,2,3)
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00'
>>> struct.pack(">llh",1,2,3)
b'\x00\x00\x00\x01\x00\x00\x00\x02\x00\x03'
>>> 
# 使用平台本地字节顺序、size大小、对齐方式
>>> struct.pack("@llh",1,2,3)
b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00'
>>> 

示例

下面例子展示了如何通过struct模块将对象存储到二进制文件中,以及在二进制文件中读取对象:

import struct
import sys
import os


class Student:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        assert isinstance(age, int), "age must be integer"
        self.__age = age

    #  将对象写入二进制文件中
    def export_binary(self, filename):

        # 对于str类型,如果不进行该步骤,则会出现export error:argument for 's' must be a bytes object
        def packing_string(name):
            return name.encode("utf8")

        fh = None
        try:
            # 指定文件操作为二进制写操作
            fh = open(filename, "wb")
            # self.name为str类型,使用struct.pack8()时,必须将str类型转换为bytes类型,因此使用一个局部方法进行转换
            data = packing_string(self.name)
            data_len = len(data)
            fh.write(struct.pack("<{0}si".format(data_len), data, self.age))
            return True
        except Exception as err:
            print("{0}:export error:{1}".format(os.path.basename(sys.argv[0]), err))
            return False
        finally:
            if fh is not None:
                fh.close()

    def import_binary(self, filename):
        fh = None
        try:
            # 二进制文件读操作
            fh = open(filename, "rb")
            if fh is not None:
                s = fh.read()
                ss = struct.unpack("<{0}si".format(len(s)-4), s)
                print(ss)  # (b'zhangsan', 21)
                return True
        except Exception as err:
            print("{0}:export error:{1}".format(os.path.basename(sys.argv[0]), err))
            return False
        finally:
            if fh is not None:
                fh.close()


if __name__ == "__main__":
    s1 = Student("zhangsan", 21)
    s2 = Student("Lisi", 23)
    s1.export_binary("s1.bin")
    s1.import_binary("s1.bin")

参考文档:https://docs.python.org/3/library/struct.html

你可能感兴趣的:(Python,Python,struct模块,struct进行二进制读写)