将数据解析为结构

Parsing data into struct

本文关键字:结构 数据      更新时间:2023-10-16

我正在做一个项目,我需要通过TCP/IP协议与设备进行通信。该设备发送大量数据,我想以某种方式将其解析为某些对象/结构。

数据包示例(在 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设备编码,因此它必须是objective-C或C(++(。

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

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 的长度。我的计划是对接收到的数据的所有其他部分重复此过程。

您将遇到的最大问题是结构不会以完全连续的形式存储数据,它们会根据单词边界对齐数据

这意味着,如果缓冲区一开始不包含结构,

则不能简单地定义一个结构,然后将 buffer[] 强制转换为该结构。 相反,您可能需要做的是声明一个结构,然后使用指针偏移量一次在一个字段中对缓冲区 [] 的每个部分进行 memcpy。

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

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

// 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;
}
通常的

方法是编写方法nextIntnextDouble等,这些方法将从流中读取字节(以正确的"字节序"顺序(并返回指定类型的值,将指针或索引更新到数组中。 这比尝试内联转换更易于管理,并且可能非常有效。 (你可以使方法C++与Objective-C相比提高效率。