将数据解析为struct

时间:2012-05-01 07:44:23

标签: c++ objective-c ios

我正在开展一个项目,我需要通过TCP / IP协议与设备进行通信。 设备发送大量数据,我不知何故想要解析为某些对象/结构。

Datapackage示例(在TCP缓冲区[]中):

[64] [1] [78] [244] [77] [189] [249] [149] hcurrent
[64] [1] [78] [247] [89] [95] [104] [85] htarget
[0] [0] [0] [0] [0] [0] [0] [0] qcurrent
[188] [220] [97] [3] [66] [62] [0] [0] kcurrent
[66] [0] [102] [103] [66] [99][153] [154] mcurrent
[253] [191] [246] [74] [170] [216] [242] [29] fmode
[102] [191] [246] [74] [178] [44] [92] [72] tmil
[137] mode

现在这个包裹框架被标识为:

 double hcurrent
 double htarget
 double qcurrent
 float kcurrent
 float mcurrent
 float fmode
 float tmil
 unsigned char mode

我的想法是,我不知何故可以将数据直接解析为具有与上述相同结构的结构。 当然,有必要确定一些关键值来确定它是哪种数据。

如何做到这一点?

由于我正在为iOS设备编码,因此它必须是客观的C或C(++)。

EDIT(测试将数据报的每个部分复制到struct中的方法): 小Java实现,我尝试读取前4个字节[0] [0] [1] [5]

byte[] read = new byte[4]; 
int length = 0;
while (length < read.length) {
    len = iStream.read(read, len, read.length);
}
int ByteLength = (int)unsignedIntToLong(read);
ByteLength = ByteLength-5;
state = 1; // Continue and work with next data.

位操作方法:

public long unsignedIntToLong(byte[] b) 
{
    long l = 0;
    l |= b[0] & 0xFF;
    l <<= 8;
    l |= b[1] & 0xFF;
    l <<= 8;
    l |= b[2] & 0xFF;
    l <<= 8;
    l |= b[3] & 0xFF;
    return l;
}

所以我获取前面提到的前4个字节,它决定了特定的东西,最后找到了465的长度。我的计划是重复此过程以及所接收数据的所有其他部分。

2 个答案:

答案 0 :(得分:4)

你将遇到的最大问题是结构不能以完全连续的形式存储数据,它们会对齐数据acording to word boundaries

这意味着你不能简单地定义一个结构,然后如果缓冲区不包含一个开头的结构,则将缓冲区[]强制转换为它。相反,您可能需要做的是声明一个结构,然后使用apointer偏移量在缓冲区[]中一次一个字段地记忆缓冲区[]的每个部分。

如果这种方法过于繁琐,通常可以关闭结构对齐,以便结构可以表示完全打包的数据。 MSVC允许使用#pragma pack来执行此操作。但是,这种方法会减慢对结构的内存访问速度。

编辑:这是一个示例,它显示了如何使用模板函数从缓冲区读取任何类型,然后将偏移更新到该缓冲区。您可以使用此方法将任意数量的类型安全地逐个解析为结构:

// We want to copy raw data to this structure
// but the short will cause it to be unaligned
struct _parsed_structure
{
    int a;
    int b;
    short c;
    int d;
} parsed_structure;

template<typename T>
void read_and_update_offset (int & offset, char * buffer, T & var)
{
    T * pInt = (T*)(buffer + offset);
    var = *pInt;
    offset += sizeof(T);
};

int _tmain(int argc, _TCHAR* argv[])
{
    // Here's a buffer which we know contains ints and shorts, we could just cast it to our structure
    // but this will cause errors because the structure will not be aligned properly.
    char buffer[] = { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0 };

    // Read the first int from the buffer into the structure
    int offset = 0;
    read_and_update_offset(offset, buffer, parsed_structure.a);
    read_and_update_offset(offset, buffer, parsed_structure.b);
    read_and_update_offset(offset, buffer, parsed_structure.c);
    read_and_update_offset(offset, buffer, parsed_structure.d);

    // Print the values
    std::cout << 
        parsed_structure.a << " " <<
        parsed_structure.b << " " <<
        parsed_structure.c << " " <<
        parsed_structure.d << " " << std::endl;

    // Look the size of our structure is different than the size of our buffer due to alignment
    std::cout <<
        "sizeof(buffer)" << "==" << sizeof(buffer) << " " <<
        "sizeof(parsed_structure)" << "==" << sizeof(parsed_structure) << std::endl;

    return 0;
}

答案 1 :(得分:2)

通常的方法是编写方法nextIntnextDouble等,它们将从流中读取字节(以正确的“endian”顺序)并返回指定类型的值,将指针或索引更新为数组。这比尝试内联转换更易于管理,并且可以非常高效。 (您可以使用C ++与Objective-C的方法来提高效率。)