将字节数组反序列化为结构

时间:2009-02-06 11:15:04

标签: c++ struct bytearray serialization

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

#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;
}

假设结构永远不包含任何可变长度字段,这总是有效吗?是否有独立/惯用的平台方式?

注意:

我在互联网上看到了很多允许你序列化/反序列化的库,但我得到的印象是,如果以前用同一个库序列化它们,它们只能反序列化。好吧,我无法控制传输的格式。我肯定会得到一个byte / char数组,其中所有的值都是相互跟随的。

6 个答案:

答案 0 :(得分:5)

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

即使在常见的x86压缩结构上也会导致代码运行得更慢。

使用不同的字节顺序平台时,您也必须小心。

顺便说一下,如果你想要一个简单且独立于平台的通信机制,并且绑定到许多编程语言,那么看看YAMI

答案 1 :(得分:5)

只是简单的复制很可能会破坏,至少如果数据来自不同的架构(甚至只是编译器)而不是你所使用的。这是出于以下原因:

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

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

答案 2 :(得分:2)

#pragma pack(1)指令应该适用于大多数编译器,但你可以通过计算你的数据结构应该有多大来检查(如果我的数学是正确的话,你的情况是10)并使用printf("%d", sizeof(Header));来检查包装正在进行中。

正如其他人所说,如果你在架构之间走路,你仍然需要警惕Endianness。

答案 3 :(得分:1)

我强烈反对逐字节读取的想法。如果您在结构声明中处理结构打包,则可以毫无问题地复制到结构中。对于endiannes问题,再次逐字节读取解决了问题,但没有给出通用的解决方案。那种方法非常蹩脚。我之前做过类似的工作,并且工作正常,没有任何故障。

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

例如,您在上面给出的结构的定义是“s s s”。 (s = short,i = int)然后我将这个结构的结构地址,这个定义和结构包装选项提供给一个处理endiannes事物的特殊函数,并且它完成了。

SwitchEndianToBig(&amp; header,“s s s”,4); // 4 =结构包装选项

答案 4 :(得分:1)

告诉我,如果我错了,但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),以确保每种类型的大小相同。

答案 5 :(得分:0)

我知道我与谁沟通,所以我真的不必担心字节序。但我还是希望远离编译器特定的命令。

那怎么样:

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;
}

摆脱了pragma,但它并不像我想的那样通用。每次向标题添加字段时,都必须修改&lt;&lt;功能。你能以某种方式迭代结构域,得到字段的类型并调用相应的函数吗?