更改C ++中整个结构的字节序

时间:2018-08-28 16:32:46

标签: c++ struct deserialization endianness

我正在用C ++编写一个解析器来解析一个定义明确的二进制文件。我已经声明了所有必需的结构。而且由于我只对特定字段感兴趣,因此在我的结构中,我通过创建大小等于已跳过字节的char数组来跳过了不需要的字段。所以我只是读取char数组中的文件,并将char指针转换为我的struct指针。现在的问题是该二进制文件中的所有数据字段都按大端顺序排列,因此在类型转换之后,我需要更改所有结构字段的字节序。一种方法是针对每个字段手动执行此操作。但是有许多结构包含很多字段,因此手动执行将非常麻烦。那么实现此目标的最佳方法是什么。而且由于我将解析非常大的此类文件(例如在TB中),因此我需要一种快速的方法来实现这一点。

编辑:我已经使用了属性(打包),因此无需担心填充问题。

3 个答案:

答案 0 :(得分:1)

如果您可以无错地进行错位访问,并且您不介意编译器或平台特定的技巧来控制填充,则可以使用。 (我假设您 对此表示同意,因为您提到__attribute__((packed)))。

在这种情况下,最好的方法是为原始数据类型编写值包装器,并在首先声明结构时使用这些包装器而不是原始类型。请记住,值包装程序必须是平凡的/类似于POD的,这样才能起作用。如果您拥有POSIX平台,则可以使用ntohs/ntohl进行字节序转换,无论您自己编写什么,它都可能会得到更好的优化。

如果未对齐的访问在您的平台上是非法的或速度缓慢,则需要反序列化。由于我们还没有反射,因此可以使用相同的值包装器(加上Ignore<N>占位符来完成此操作,该占位符会跳过您不感兴趣的字段的N个字节),并在元组而不是结构中声明它们-您可以遍历元组中的成员,并告诉每个成员对消息进行反序列化。

答案 1 :(得分:0)

如果您可以列出需要字节序转换的字段的偏移量(以字节为单位,相对于文件顶部)以及这些字段的大小,则可以进行所有直接在char数组上进行带有单个for循环的endian转换。例如。像这样的东西(伪代码):

struct EndianRecord {
   size_t offsetFromTop;
   size_t fieldSizeInByes;
};

std::vector<EndianRecord> todoList;
// [populate the todo list here...]

char * rawData = [pointer to the raw data]
for (size_t i=0; i<todoList.size(); i++)
{
   const EndianRecord & er = todoList[i];
   ByteSwap(&rawData[er.offsetFromTop], er.fieldSizeBytes);
}

struct MyPackedStruct * data = (struct MyPackedStruct *) rawData;
// Now you can just read the member variables
// as usual because you know they are already
// in the correct endian-format.

...当然,最困难的部分是提出正确的todoList,但是由于文件格式定义明确,因此应该可以通过算法生成它(或者更好的是,将其创建为生成器,其中包含可以调用的GetNextEndianRecord()方法,这样就不必在内存中存储很大的向量)

答案 2 :(得分:0)

一种实现方法是将C预处理程序与C ++运算符结合在一起。编写几个这样的C ++类:

#include "immintrin.h"

class FlippedInt32
{
    int value;
public:
    inline operator int() const
    {
        return _bswap( value );
    }
};

class FlippedInt64
{
    __int64 value;
public:
    inline operator __int64() const
    {
        return _bswap64( value );
    }
};

然后

#define int FlippedInt32

包括定义这些结构的标题之前。 #undef之后的#include

这将用int替换结构中的所有FlippedInt32字段,该字段的大小相同,但返回翻转的字节。

如果您可以修改自己的结构,则不需要预处理器部分。只需将整数替换为字节翻转类即可。