struct.pack——打包为二进制数据方法

 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;      // 输出 42
    std::cout << "float value: " << floatValue << std::endl;  // 输出 3.14

    return 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++ 之间传递数据,可以按照以下步骤进行:

  1. 在 Python 中使用 struct.pack 打包数据:

    打包后的数据是字节序列,比如 b'\x2a\x00\x00\x00\xc3\xf5\x48\x40'
  2. 在 C++ 中使用 memcpy 解包数据:

    你只需要将 Python 传过来的字节数据逐字节拷贝到 C++ 的内存中(如 char[] 数组),然后使用 memcpy 提取具体的类型值(如 intfloat)。

示例:从 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;      // 输出 42
    std::cout << "float value: " << floatValue << std::endl;  // 输出 3.14

    return 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++ 中的字符串字面量形式,两者是完全等价的,只是表示方式不同。 

分解来看

  1. b'*\x00\x00\x00'

    • * 是 ASCII 字符,对应的十六进制值为 \x2a
    • \x00\x00\x00 表示 3 个零字节。
    • 所以在 C++ 中等价为 "\x2a\x00\x00\x00"
  2. 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 表,分别对应 0x480x40,所以分别为 \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@'):

  1. 使用 ASCII 表或者查表方法手动转换字符到十六进制表示。
  2. 编写 Python 脚本自动转换为 C++ 字符串字面量格式("\x..")。
  3. 对于浮点数、整数等可以查阅 IEEE 754 或者利用 Python 自动解析。

这样,你就可以从 Python 的打印值快速得到 C++ 字符串字面量格式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cici_ovo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值