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++ 字符串字面量格式。