Python 常用模块struct

【一】介绍

  • struct 模块提供了一种处理二进制数据的方式
  • 它允许你使用类似于C语言的结构体的方式来打包(pack)和解包(unpack)数据
  • 这对于处理二进制文件、网络协议等场景非常有用

【二】使用

【1】打包

(1)讲解
  • struct.pack(fmt, v1, v2......)

  • 作用:

    • fmt是格式字符串,指定需要打包的数据格式
    • v1,v2,…是待打包的数据
    • 打包以后的数据格式为字节流
  • 格式化表格来自于官网:struct — Interpret bytes as packed binary data — Python 3.12.1 documentation

Format C Type Python type Standard size Notes
x pad byte no value (7)
c char bytes of length 1 1
b signed char integer 1 (1), (2)
B unsigned char integer 1 (2)
? _Bool bool 1 (1)
h short integer 2 (2)
H unsigned short integer 2 (2)
i int integer 4 (2)
I unsigned int integer 4 (2)
l long integer 4 (2)
L unsigned long integer 4 (2)
q long long integer 8 (2)
Q unsigned long long integer 8 (2)
n ssize_t integer (3)
N size_t integer (3)
e (6) float 2 (4)
f float float 4 (4)
d double float 8 (4)
s char[] bytes (9)
p char[] bytes (8)
P void* integer (5)
(2)代码演示
  • python基础数据类型单独打包
import struct

int_value = 123156456
float_value = 4561.1123
bool_value = True


# 整型打包以后占用四个字节
int_struct = struct.pack('i', int_value)
print(int_struct, len(int_struct))

# 浮点型打包以后占用四个字节
float_struct = struct.pack('f', float_value)
print(float_struct, len(float_struct))

# 布尔型打包以后占用四个字节
bool_struct = struct.pack('?', bool_value)
print(bool_struct, len(bool_struct))

  • python字符串数据类型打包
import struct

str_value = "da456q1"

# 字符串格式需要转换成字节格式才可以打包,并且还需要提供字符串的长度,默认为1
# 提供的长度小于实际字符串的长度将打包部分数据,造成打包不完整
str_struct = struct.pack('7s', str_value.encode("utf8"))
print(str_struct, len(str_struct))
  • python基础数据类型一次性打包
    • 注意:fmt格式的顺序要和*v的顺序一致
import struct

int_value = 123156456
float_value = 4561.1123
bool_value = True
str_value = "da456q1"

# 一次打包多个
all_struct = struct.pack('i f ? 2s', int_value, float_value, bool_value, str_value.encode("utf8"))
print(all_struct, len(all_struct))

【2】解包

(1)讲解
  • struct.unpack(fmt, v1, v2......)
  • 作用:
    • fmt是格式字符串,需要知道打包的格式
    • v1,v2,…是待打包的数据
    • 解包以后得到一个元组
(2)代码演示
  • 单独数据解包
    • 返回元组,数据在元组第一个位置
import struct

int_struct = struct.pack('i', 123156456)
float_struct = struct.pack('f', 4561.1123)
bool_struct = struct.pack('?', True)
str_struct = struct.pack('7s', "da456q1".encode("utf8"))

# 解包后类型正常
int_res = struct.unpack("i", int_struct)
print(int_res, type(int_res[0]))

float_res = struct.unpack("f", float_struct)
print(float_res, type(float_res[0]))

bool_res = struct.unpack("?", bool_struct)
print(bool_res, type(bool_res[0]))

str_res = struct.unpack("7s", str_struct)
print(str_res, type(str_res[0]))
  • 混合数据解包
    • 按照打包顺寻返回一个元组
import struct

int_value = 123156456
float_value = 4561.1123
bool_value = True
str_value = "da456q1"

all_struct = struct.pack('i f ? 7s', int_value, float_value, bool_value, str_value.encode("utf8"))

res = struct.unpack('i f ? 7s', all_struct)
print(res)
# (123156456, 4561.1123046875, True, b'da456q1')

【三】应用

  • 问题:

    • 在socket模块的TCP协议传输数据中

    • 由于接收方不知道将要收到多大的数据,而导致数据读取可能不完整

    • 出现粘包问题,struct模块就可以用来解决这个问题

  • 解决办法:

    • 在每次发送数据之前就行数据大小计算
    • 计算的结果(长度)通过struct计算得到一个四字节的字节流
    • 通过发送这个固定大小的字节流
    • 接收端可以知道将要收到的数据的大小
    • 保证了可以将数据完整读出
# 服务端
import socket
import struct

# 1320KB的数据内容
big_data = ("重要信息" * 110).encode("utf8")
data_size = len(big_data)
data_size_struct = struct.pack("i", data_size)

# 创建socket对象
server = socket.socket()
server.bind(("localhost", 5656))
server.listen()
conn, addr = server.accept()

# 先发送大小数据
conn.send(data_size_struct)
# 发送大数据包
conn.send(big_data)

# 关闭
conn.close()
server.close()
# 客户端
import socket
import struct

client = socket.socket()
client.connect(("localhost", 5656))

# 读取大小文件
head = client.recv(4)
total = struct.unpack("i", head)[0]

# 根据大小接收数据
have = 0
data = bytes()
while have < total:
    data += client.recv(1024)
    have += 1024

print(data.decode("utf8"))

client.close()

你可能感兴趣的:(Python,模块和包,python,开发语言,tcp/ip,网络)