了解c语言的人,一定会知道struct结构体在c语言中的作用,它定义了一种结构,里面包含不同类型的数据(int,char,bool等等),方便对某一结构对象进行处理。而在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,不必担心太多的问题,而当传递诸如int、char之类的基本数据的时候,就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。python中的struct模块就提供了这样的机制,该模块的主要作用就是对python基本类型值与用python字符串格式表示的C struct类型间的转化
This module performs conversions between Python values and C structs represented as Python strings.
一、struct对象
struct.Struct(format)
返回一个struct对象(结构体,参考C)。该对象可以根据格式化字符串的格式来读写二进制数据。
第一个参数(格式化字符串)可以指定字节的顺序。默认是根据系统来确定,也提供自定义的方式,只需要在前面加上特定字符即可:
struct.Struct('>I4sf')
struct模块中最重要的三个函数是pack(), unpack(), calcsize()
pack(fmt, v1, v2, ...)
:按照给定的格式(fmt),把数据封装成字符串(实际上是类似于 c 结构体的字节流);
unpack(fmt, [string])
:按照给定的格式(fmt)解析字节流string,返回解析出来的tuple;
calcsize(fmt)
:计算给定的格式(fmt)占用多少字节的内存;
1、基本的pack和unpack
struct提供用format specifier方式对数据进行打包和解包(Packing and Unpacking)。
pack(v1, v2, …)
按照fmt(格式化字符串)的格式来打包参数v1,v2,...。返回一个字节流对象。
示例
- 首先,定义了一个元组数据,包含int、string、float三种数据类型;
- 然后,定义了struct对象,并制定了format‘I3sf’,I 表示int,3s表示三个字符长度的字符串,f 表示 float;
- 最后,通过struct的pack和unpack进行打包和解包。
import struct
import binascii
values = (1, 'abc', 2.7)
s = struct.Struct('I3sf')
packed_data = s.pack(*values)
unpacked_data = s.unpack(packed_data)
print('Original values:', values)
print('Format string :', s.format)
print('Uses :', s.size, 'bytes')
print('Packed Value :', binascii.hexlify(packed_data))
print('Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data)
输出:
Original values: (1, 'abc', 2.7)
Format string : I3sf
Uses : 12 bytes
Packed Value : 0100000061626300cdcc2c40
Unpacked Type : <type 'tuple'> Value: (1, 'abc', 2.700000047683716)
通过输出结果可以发现,value被pack之后,转化为了一段二进制字节串,而unpack可以把该字节串再转换回一个元组。
但是值得注意的是对于float的精度发生了改变,这是由一些比如操作系统等客观因素所决定的。打包之后的数据所占用的字节数与C语言中的struct十分相似。13是字符串+1,也就是( 4+3+4=11) + 1。
这里使用到了binascii.hexlify(data)函数。
binascii.hexlify(data)
返回字节流的十六进制字节流。
>>> a = 'hello'
>>> b = a.encode()
>>> b
b'hello'
>>> c = binascii.hexlify(b)
>>> c
b'68656c6c6f'
format格式化对照表
格式字符串(format string)由一个或多个格式字符(format characters)组成

提示:
- signed char(有符号位)取值范围是 -128 到 127(有符号位)
- unsigned char (无符号位)取值范围是 0 到 255
2.字节顺序,大小和校准
在Format string 的首位,有一个可选字符来决定大端和小端,列表如下:

如果没有附加,默认为@,即使用本机的字符顺序(大端or小端),对于C结构的大小和内存中的对齐方式也是与本机相一致的(native),比如有的机器integer为2位而有的机器则为四位;有的机器内存对其位四位对齐,有的则是n位对齐(n未知,我也不知道多少)。
还有一个标准的选项,被描述为:如果使用标准的,则任何类型都无内存对齐。
比如刚才的小程序的后半部分,使用的format string中首位为!,即为大端模式标准对齐方式,故而输出的为'\x00\x00\x00\x01\x00\x02\x03',其中高位自己就被放在内存的高地址位了。
网友评论