背景:将物联网采集设备参数数据按照通信协议数据类型要求序列化成字节流推送到MQTT服务器。
Python 中有一个名为 Struct 的标准库,它可以执行 Python 值和以 Python 字节对象表示的 C 结构之间的转换。Python不适合编写底层操作字节流的代码,但在对性能要求不高的地方,利用struct就方便多了。
Python之Struct模块详解
1、struct模块作用
-
序列化:按照指定格式将Python数据转换为字节流字符串;【网络传输数据的基本形式是字节,也就是Byte。一个字节就是8个二进制位,8个Bit】
-
反序列化:按照指定格式将字节流转换为Python指定的数据类型;
-
用来处理存储在文件中或是从网络连接等其他来源获取的二进制数据:需要用'wb','rb'以二进制(字节流)写,读的方式来处理文件;
2、struct模块中的函数
函数 | return | explain |
---|---|---|
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) | size of fmt | 计算给定的格式(fmt)占用多少字节的内存,注意对齐方式 |
备注:
当打包或者解包的时,需要按照特定的方式来打包或者解包.该方式就是格式化字符串,它指定了数据类型,除此之外,还有用于控制字节顺序、大小和对齐方式的特殊字符.
1)、fmt格式符
格式符 | C 类型 | PYTHON 类型 | STANDARD SIZE |
---|---|---|---|
x |
pad byte | no value | |
c |
char |
string of length 1 | 1 |
b |
signed char |
integer | 1 |
B |
unsigned char |
integer | 1 |
? |
_Bool |
bool | 1 |
h |
short |
integer | 2 |
H |
unsigned short |
integer | 2 |
i |
int |
integer | 4 |
I |
unsigned int |
integer | 4 |
l |
long |
integer | 4 |
L |
unsigned long |
integer | 4 |
q |
long long |
integer | 8 |
Q |
unsigned long long |
integer | 8 |
f |
float |
float | 4 |
d |
double |
float | 8 |
s |
char[] |
string | |
p |
char[] |
string | |
P |
void * |
integer |
2)对齐方式
为了同c中的结构体交换数据,还要考虑c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.
Character | Byte order | Size | Alignment |
---|---|---|---|
@(默认) | 本机 | 本机 | 本机,凑够4字节 |
= | 本机 | 标准 | none,按原字节数 |
< | 小端 | 标准 | none,按原字节数 |
大端 | 标准 | none,按原字节数 | |
! | network(大端) | 标准 | none,按原字节数 |
****Struct模块打包和解包示例
1、进制转换问题引入
python
res = int(input("请输入一个数字"))
print("十进制数字是:",res)
print("转换为二进制数据为:",bin(res))
print("转换为十进制数据为",hex(res))
请输入一个数字5
十进制数字是:5
转换为二进制数据为: 0b101
转换为十进制数据为 0x5
Python没有专门处理字节的数据类型。但由于b'str'可以表示字节,所以,字节数组=二进制str。而在C语言中,我们可以很方便地用struct、union来处理字节,以及字节和int,float的转换。
在Python中,比方说要把一个32位无符号整数变成字节,也就是4个长度的bytes,你得配合位运算符这么写:
ini
#32位无符号整数变成字节
R=[_ for _ in range(4)]
temp = 10240099
R[0] = int(temp / 256 / 256 / 256)
temp = temp % (256 * 256 * 256)
R[1] = int(temp / 256 / 256)
temp = temp % (256 * 256)
R[2] = int(temp / 256)
temp = temp % 256
R[3] = temp
print(bytes(R))
【b'\x00\xa5g\xc0'】
所以转换很麻烦。如果换成浮点数就无能为力了,只能转换为整型,如果是有符号的,还需要加条件判断,所以为了使得这一过程变得简洁,Python有专门处理数据类型转换的模块struct。
2、struct.pack打包和struct.unpack解包
Python提供了一个struct模块来解决bytes和其他二进制数据类型的转换。
- 按指定格式将 Python 数据转换为字节流数据,对应函数为struct.pack
- 将字节流数据转换为指定的 Python 数据类型,对应函数为struct.unpack
固定长度的整型,包括有符号整型或无符号整型。【浮点型可转换为整型】
整型范围
Int8 - [-128 : 127]
Int16 - [-32768 : 32767]
Int32 - [-2147483648 : 2147483647]
Int64 - [-9223372036854775808 : 9223372036854775807]
无符号整型范围
UInt8 - [0 : 255]
UInt16 - [0 : 65535]
UInt32 - [0 : 4294967295]
UInt64 - [0 : 18446744073709551615]
示例: struct的pack函数把任意数据类型变成bytes及unpack函数把bytes还原其他数据:
ini
import struct
#转字节码
def generate_bytes(arr):
"""
生成字节数流
:param arr:
:return:
uint8 = B
int8 = b
uint16 = H
int16 = h
uint32 = I
int32 = i
< 小端
> 大端
"""
fmt = ">BHibI"
to_bytes = struct.pack(fmt, *arr)
print("字节数据",to_bytes)
bytes_str = struct.unpack(fmt, to_bytes)
print("还原数据",bytes_str)
return to_bytes,bytes_str
if __name__ == '__main__':
# 测试数据
arr = [5, 6500, 2147483647, -10, 8900023]
generate_bytes(arr)
输入数据 [5, 6500, 2147483647, -10, 8900023]
字节数据 b'\x05\x19d\x7f\xff\xff\xff\xf6\x00\x87\xcd\xb7'
还原数据 (5, 6500, 2147483647, -10, 8900023)
此处>表示字节顺序为 big-endian(大端)。