Python's struct.pack/unpack 等价性 in C++

Python's struct.pack/unpack equivalence in C++

本文关键字:unpack C++ in pack struct Python      更新时间:2023-10-16

我使用了struct。将数据转换为序列化的字节流。

>>> import struct
>>> struct.pack('i', 1234)
'xd2x04x00x00'

c++中的等价是什么?

从长远来看,使用第三方库(例如Google Protocol Buffers)可能会更好,但如果您坚持自己编写,那么您的示例的c++版本可能是这样的:

#include <stdint.h>
#include <string.h>
int32_t myValueToPack = 1234;  // or whatever
uint8_t myByteArray[sizeof(myValueToPack)];
int32_t bigEndianValue = htonl(myValueToPack);  // convert the value to big-endian for cross-platform compatibility
memcpy(&myByteArray[0], &bigEndianValue, sizeof(bigEndianValue));
// At this point, myByteArray contains the "packed" data in network-endian (aka big-endian) format

对应的'unpack'代码看起来像这样:

// Assume at this point we have the packed array myByteArray, from before
int32_t bigEndianValue;
memcpy(&bigEndianValue, &myByteArray[0], sizeof(bigEndianValue));
int32_t theUnpackedValue = ntohl(bigEndianValue);

在现实生活中,您可能会打包多个值,这很容易做到(通过增大数组大小并在循环中调用htonl()和memcpy()—不要忘记增加memcpy()的第一个参数,以便您的第二个值不会覆盖第一个值在数组中的位置,等等)。

您可能还想打包(也就是序列化)不同的数据类型。Uint8_t的(又名字符)和布尔值是足够简单的,因为没有尾端处理是必要的——你可以把它们每一个复制到数组中作为一个字节。Uint16_t可以通过htons()转换为大端进制,通过ntohs()转换回本机进制。浮点值有点棘手,因为没有内置的htonf(),但是您可以在符合ieee754的机器上运行自己的值:

uint32_t htonf(float f)
{
   uint32_t x;
   memcpy(&x, &f, sizeof(float));
   return htonl(x);
}

…以及相应的ntohf()来解压缩它们:

float ntohf(uint32_t nf)
{
   float x;
   nf = ntohl(nf);
   memcpy(&x, &nf, sizeof(float));
   return x;
}

最后,对于字符串,您可以通过memcpy:

将字符串的字节数添加到缓冲区(包括NUL终止符)。
const char * s = "hello";
int slen = strlen(s);
memcpy(myByteArray, s, slen+1);  // +1 for the NUL byte

没有。c++没有内置序列化。

您必须将单个对象写入字节数组/向量,并且要注意字节顺序(如果您希望代码可移植)。

https://github.com/karkason/cppystruct

#include "cppystruct.h"
// icmp_header can be any type that supports std::size and std::data and holds bytes
auto [type, code, checksum, p_id, sequence] = pystruct::unpack(PY_STRING("bbHHh"), icmp_header);
int leet = 1337;
auto runtimePacked = pystruct::pack(PY_STRING(">2i10s"), leet, 20, "String!");
// runtimePacked is an std::array filled with "x00x00x059x00x00x00x10String!x00x00x00"
// The format is "compiled" and has zero overhead in runtime
constexpr auto packed = pystruct::pack(PY_STRING("<2i10s"), 10, 20, "String!");
// packed is an std::array filled with "x00x01x00x00x10x00x00x00String!x00x00x00"

您可以查看Boost。序列化,但我怀疑你能否让它使用与Python包相同的格式。

我也在寻找同样的东西。幸运的是我找到了https://github.com/mpapierski/struct

可以添加一些缺失的类型到struct.hpp,我认为这是目前为止最好的。

要使用它,只需像这样定义参数

DEFINE_STRUCT(test,
    ((2, TYPE_UNSIGNED_INT))
    ((20, TYPE_CHAR))
    ((20, TYPE_CHAR))
)

直接调用编译时生成的函数

pack(unsigned int p1, unsigned int p2, const char * p3, const char * p4)

参数的数量和类型将取决于您上面定义的内容。返回类型是包含打包数据的char*。还有另一个unpack()函数可以用来读取缓冲区

您可以使用union在相同的内存中获得不同的视图。

例如:

union Pack{
   int i;
   char c[sizeof(int)];
};
Pack p = {};
p.i = 1234;
std::string packed(p.c, sizeof(int)); // "xd2x04x00"

正如在其他答案中提到的,您必须注意顺序。