【问题标题】:Deserialize a byte array to a struct将字节数组反序列化为结构
【发布时间】:2010-10-05 22:00:25
【问题描述】:

我通过网络得到一个字符/字节数组的传输。它包含一个标题和一些数据。我想将标题映射到结构上。这是一个例子:

#pragma pack(1)

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < sizeof(Header); x++)
        msgBuffer[x] = 0x01; // assign some values

    memcpy(&header, msgBuffer.data(), sizeof(Header));

    system("PAUSE");    

    return 0;
}

假设结构从不包含任何可变长度字段,这是否总是有效?是否有独立于平台/惯用的方式来做到这一点?

注意:

我在互联网上看到了很多可以让您序列化/反序列化的库,但我的印象是它们只能反序列化之前使用同一个库序列化的某些内容。好吧,我无法控制传输的格式。我肯定会得到一个字节/字符数组,其中所有值都相互跟随。

【问题讨论】:

    标签: c++ struct bytearray serialization


    【解决方案1】:

    某些处理器要求某些类型正确对齐。他们将不接受指定的打包并生成硬件陷阱。

    甚至在常见的 x86 打包结构上也会导致代码运行更慢。

    此外,在使用不同的字节序平台时,您必须小心。

    顺便说一句,如果您想要一个简单且独立于平台的通信机制,并绑定到多种编程语言,请查看YAMI

    【讨论】:

      【解决方案2】:

      如果我错了,请告诉我,但是 AFAIK,这样做可以保证您的数据是正确的 - 假设类型在您的不同平台上具有相同的大小

      #include <array>
      #include <algorithm>
      
      //#pragma pack(1) // not needed
      
      struct Header
      {
          unsigned short bodyLength;
          int msgID;
          unsigned short someOtherValue;
          unsigned short protocolVersion;
          float testFloat;
      
          Header() : bodyLength(42), msgID(34), someOtherValue(66), protocolVersion(69), testFloat( 3.14f ) {}
      };
      
      int main()
      {
          std::tr1::array<char, 128> msgBuffer;
          Header header;
      
          const char* rawData = reinterpret_cast< const char* >( &header );
      
          std::copy( rawData, rawData + sizeof(Header), msgBuffer.data()); // assuming msgBuffer is always big enough
      
          system("PAUSE");    
      
          return 0;
      }
      

      如果目标平台上的类型不同,则必须为每种类型使用别名 (typedef) 以确保每种使用的类型的大小相同。

      【讨论】:

      • 嗯,你正在反过来(将结构转换为字节数组)。即使这样也不能正常工作,因为您将结构的填充复制到数组中。
      【解决方案3】:

      我强烈反对逐字节读取的想法。如果您在结构声明中处理结构包装,则可以毫无问题地复制到结构中。对于字节序问题,再次逐字节读取可以解决问题,但不会为您提供通用解决方案。这种方法很鸡肋。我以前做过类似的工作,但它工作得很好,没有出现故障。

      考虑一下。我有一个结构,我也有那个结构的相应定义。您可以手动构建它,但我已经为此编写了一个解析器并将其用于其他事情。

      例如,你上面给出的结构的定义是“s i s s”。 ( s = short , i = int ) 然后我将结构地址、这个结构的这个定义和结构打包选项提供给一个处理字节序的特殊函数,瞧,它就完成了。

      SwitchEndianToBig(&header, "s i s s", 4); // 4 = 结构封装选项

      【讨论】:

        【解决方案4】:

        我知道我在与谁交流,所以我真的不必担心字节顺序。但无论如何我都喜欢远离编译器特定的命令。

        那么这个怎么样:

        const int kHeaderSizeInBytes = 6;
        
        struct Header
        {
            unsigned short bodyLength;
            unsigned short msgID;
            unsigned short protocolVersion; 
        
            unsigned short convertUnsignedShort(char inputArray[sizeof(unsigned short)])
                {return (((unsigned char) (inputArray[0])) << 8) + (unsigned char)(inputArray[1]);}
        
            void operator<<(char inputArray[kHeaderSizeInBytes])
            {
                bodyLength = convertUnsignedShort(inputArray);
                msgID = convertUnsignedShort(inputArray + sizeof(bodyLength));
                protocolVersion = convertUnsignedShort(inputArray + sizeof(bodyLength) + sizeof(msgID));
            }
        };
        
        int main()
        {
            boost::array<char, 128> msgBuffer;
            Header header;
        
            for(int x = 0; x < kHeaderSizeInBytes; x++)
                msgBuffer[x] = x;
        
            header << msgBuffer.data();
        
            system("PAUSE");    
        
            return 0;
        }
        

        摆脱了杂注,但它不像我想要的那样通用。每次向标题添加字段时,都必须修改

        【讨论】:

        • 关于迭代结构字段:你必须使用结构吗?我问是因为用元组替换它会允许字段的迭代。
        【解决方案5】:

        #pragma pack(1) 指令应该适用于大多数编译器,但您可以通过计算出您的数据结构应该有多大(如果我的数学正确,则为 10)并使用printf("%d", sizeof(Header)); 来检查打包是否正在完成。

        正如其他人所说,如果您要在架构之间切换,您仍然需要警惕字节序。

        【讨论】:

          【解决方案6】:

          只是简单的复制很可能会破坏,至少如果数据可能来自与您所在的架构不同的架构(甚至只是编译器)。这是因为:

          第二个链接是 GCC 特定的,但这适用于所有编译器。

          我建议逐字节读取字段,并从这些字节中组装更大的字段(整数等)。这使您可以控制字节顺序和填充。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-09-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多