struct模块可在python值和以python bytes对象表示的C结构体之间进行转换。
目录
基本语法
字节顺序、大小和对齐
示例代码
场景
转换逻辑:
如何转换
示例:从 Python 传到 C++
格式转换
分解来看
将字节数据转为 C++ 字符串字面量格式
方法 1:手动转换
方法 2:自动化工具(Python 脚本辅助)
背后的计算原理
总结
详细参考struct --- 将字节串解读为打包的二进制数据 — Python 3.13.0 文档
struct.pack 是 Python 中用于将数据打包成二进制格式的函数。
它主要用于处理 C 语言结构的数据,将 Python 的数据类型转换为 C 语言的二进制数据流。这个函数在处理文件或网络的二进制流时非常有用。
基本语法
struct.pack(format, v1, v2, ...)
其中,format参数指定了数据的格式,v1, v2, ... 是要打包的值。
格式字符串中的每个字符代表一种数据类型,常见的格式字符包括:
l:表示long类型d:表示double类型x:用于占位,不产生任何输出。c:char,一个字节。b:signed char,一个字节。B:unsigned char,一个字节。h:short,两个字节。H:unsigned short,两个字节。i:int,四个字节。I:unsigned int,四个字节。f:float,四个字节。s:string,字符串。
# 例如:import struct# 打包 short, short, long 类型的数据
packed_data = struct.pack('hhl', 1, 2, 3)
print(packed_data) # 输出: b'\x01\x00\x02\x00\x03\x00\x00\x00'# 打包 char 和 int 类型的数据
packed_data = struct.pack('ci', b'*', 0x12131415)
print(packed_data) # 输出: b'*\x15\x14\x13\x12'# 可以通过len函数获取字节流的长度,并通过print函数打印输出。
print(len(packed_data))
字节顺序、大小和对齐
格式字符串还可以指定字节顺序、大小和对齐方式:
-
@ 本地字节顺序、大小和对齐
-
= 本地字节顺序,标准大小和对齐
-
< 小端字节顺序,标准大小和对齐
-
> 大端字节顺序,标准大小和对齐
-
! 网络字节顺序(大端)
# 例如:import struct# 使用网络字节顺序(大端)打包数据
packed_data = struct.pack('!ihb', 1, 2, 3)
print(packed_data) # 输出: b'\x00\x00\x00\x01\x00\x02\x03'
示例代码
以下是一个完整的示例,展示了如何使用 struct.pack 和 struct.unpack:
import struct# 打包数据
data = [1, 2, 3]
packed_data = struct.pack('!ihb', *data)
print(repr(packed_data)) # 输出: b'\x00\x00\x00\x01\x00\x02\x03'# 解包数据
unpacked_data = struct.unpack('!ihb', packed_data)
print(unpacked_data) # 输出: (1, 2, 3)
通过这些示例,可以看到 struct.pack 如何将 Python 数据类型转换为二进制数据流,并且可以通过 struct.unpack 将其还原为原始数据类型。这在处理需要与 C 语言程序或网络协议交互的数据时非常有用。
场景
在C++中,可以使用memcpy函数将数据从struct.pack的返回值复制到C++的内存中。
如果你要从 Python 中使用 struct.pack 生成更多不同类型的数据,然后在 C++ 中解析,可以参考以下示例:
import struct# 打包一个 int 和一个 float
data = struct.pack('if', 42, 3.14)
print(data) # 输出: b'*\x00\x00\x00\xc3\xf5H@'
在 Python 中,struct.pack 函数返回的是字节序列(bytes 类型),通常在 Python 中以 b'...' 这种形式表示。
‘i’ 表示 4 字节的整数 (int):
- 42 的十六进制表示是
0x2A,在内存中会表示为\x2a\x00\x00\x00(小端序)。 b'*'等价于\x2a(即 42 的 ASCII 表示),剩下的\x00\x00\x00是填充高位。
‘f’ 表示 4 字节的浮点数 (float):
- 3.14 的浮点表示在内存中是
\xc3\xf5\x48\x40(小端序)。
组合结果:struct.pack 返回的字节序列为:
b'\x2a\x00\x00\x00\xc3\xf5\x48\x40'。
#include <iostream>
#include <cstring>int main() {// Python struct.pack('if', 42, 3.14) 的返回值char data[] = "\x2a\x00\x00\x00\xc3\xf5\x48\x40";int intValue;float floatValue;// 解析 int 值memcpy(&intValue, data, sizeof(intValue));// 解析 float 值memcpy(&floatValue, data + sizeof(intValue), sizeof(floatValue));std::cout << "int value: " << intValue << std::endl; // 输出 42std::cout << "float value: " << floatValue << std::endl; // 输出 3.14return 0;
}
运行上面的C++代码,将输出42,说明数据成功从Python的结构打包转换为C++的整型数据。
char data[] = "\x2a\x00\x00\x00\xc3\xf5\x48\x40";
转换逻辑:
\x2a等价于 Python 中的b'*',对应 ASCII 码为 42。\x00\x00\x00对应的是填充的高位,表示整数 42 在小端序下的表示方法。\xc3\xf5\x48\x40是浮点数 3.14 的 IEEE 754 表示,按照小端序排列。
如何转换
如果你想在 Python 和 C++ 之间传递数据,可以按照以下步骤进行:
-
在 Python 中使用
打包后的数据是字节序列,比如struct.pack打包数据:b'\x2a\x00\x00\x00\xc3\xf5\x48\x40'。 -
在 C++ 中使用
你只需要将 Python 传过来的字节数据逐字节拷贝到 C++ 的内存中(如memcpy解包数据:char[]数组),然后使用memcpy提取具体的类型值(如int、float)。
示例:从 Python 传到 C++
假设我们使用 Python 生成字节数据,并保存到文件中,然后在 C++ 中读取文件来解析。
import struct# 打包一个 int 和一个 float
data = struct.pack('if', 42, 3.14)
# 保存为文件,模拟传递数据
with open("data.bin", "wb") as f:f.write(data)
#include <iostream>
#include <fstream>
#include <cstring>int main() {// 打开文件读取字节序列std::ifstream file("data.bin", std::ios::binary);// 存储读取的数据char data[8];file.read(data, 8);file.close();int intValue;float floatValue;// 解析 int 值memcpy(&intValue, data, sizeof(intValue));// 解析 float 值memcpy(&floatValue, data + sizeof(intValue), sizeof(floatValue));std::cout << "int value: " << intValue << std::endl; // 输出 42std::cout << "float value: " << floatValue << std::endl; // 输出 3.14return 0;
}
- 字节序列:Python 中的
b'*\x00\x00\x00\xc3\xf5H@'和 C++ 中的"\x2a\x00\x00\x00\xc3\xf5\x48\x40"是等价的表示,都是小端序。 - 转换:
struct.pack生成的bytes在 Python 中直接是二进制格式,而在 C++ 中你可以使用字符串字面量(如char[])来表示同样的二进制数据。 - 跨语言一致性:这种表示方法在 Python 和 C++ 之间是一致的,使用
memcpy解析即可,无需额外转换。
格式转换
如何从 Python 打印的字节序列得到 C++ 字符串字面量这种格式。
Python 中打印出的 b'*\x00\x00\x00\xc3\xf5H@' 实际上是字节序列的二进制表示形式,而 "\x2a\x00\x00\x00\xc3\xf5\x48\x40" 是 C++ 中的字符串字面量形式,两者是完全等价的,只是表示方式不同。
分解来看
-
b'*\x00\x00\x00'*是 ASCII 字符,对应的十六进制值为\x2a。\x00\x00\x00表示 3 个零字节。- 所以在 C++ 中等价为
"\x2a\x00\x00\x00"。
-
b'\xc3\xf5H@'- 这是浮点数
3.14的 IEEE 754 表示。 - 对应的字节顺序是:
\xc3,\xf5,H,@。 - 其中
H的 ASCII 值是\x48,@的 ASCII 值是\x40。 - 所以在 C++ 中表示为
"\xc3\xf5\x48\x40"。
- 这是浮点数
将字节数据转为 C++ 字符串字面量格式
方法 1:手动转换
如果你看到 Python 中的输出 b'*\x00\x00\x00\xc3\xf5H@',可以手动按照以下规则转换:
- 对于
*,查 ASCII 表知道十六进制为0x2a,所以对应为\x2a。 - 对于
\x00,就是字节0,不需要转换。 - 对于
H和@,查 ASCII 表,分别对应0x48和0x40,所以分别为\x48和\x40。
这给出了 C++ 中的字符串字面量:"\x2a\x00\x00\x00\xc3\xf5\x48\x40"。
方法 2:自动化工具(Python 脚本辅助)
如果你不想手动转换,可以写个 Python 脚本来输出这种格式:
import struct# 打包数据
data = struct.pack('if', 42, 3.14)# 将字节数据转为 C++ 字符串字面量格式
cpp_literal = ''.join(f'\\x{byte:02x}' for byte in data)
print(cpp_literal) # 输出:\x2a\x00\x00\x00\xc3\xf5\x48\x40
你可以直接复制输出结果,并在 C++ 代码中使用。
背后的计算原理
对于浮点数 3.14,转换成 IEEE 754 单精度浮点表示法的小端序结果为 0x4048f5c3,对应字节序列为 \xc3\xf5\x48\x40。
转换流程:
3.14的 IEEE 754 表示是0x4048f5c3。- 小端序表示时,最低有效字节放在最前面:
- 低位字节是
0xc3(十进制 195) - 次低位字节是
0xf5(十进制 245) - 次高位字节是
0x48(ASCII 为H) - 高位字节是
0x40(ASCII 为@)
- 低位字节是
所以最终的字节序列为 "\xc3\xf5\x48\x40"。
总结
通过 Python 的字节表示(如 b'*\x00\x00\x00\xc3\xf5H@'):
- 使用 ASCII 表或者查表方法手动转换字符到十六进制表示。
- 编写 Python 脚本自动转换为 C++ 字符串字面量格式(
"\x..")。 - 对于浮点数、整数等可以查阅 IEEE 754 或者利用 Python 自动解析。
这样,你就可以从 Python 的打印值快速得到 C++ 字符串字面量格式。
