Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/66.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C/C+中解析二进制消息流+;_C++_C_Parsing_Binary_Memory Alignment - Fatal编程技术网

C++ 在C/C+中解析二进制消息流+;

C++ 在C/C+中解析二进制消息流+;,c++,c,parsing,binary,memory-alignment,C++,C,Parsing,Binary,Memory Alignment,我正在为二进制协议(Javad GRIL协议)编写一个解码器。它由大约一百条消息组成,数据格式如下: struct MsgData { uint8_t num; float x, y, z; uint8_t elevation; ... }; 这些字段是ANSI编码的二进制数,它们彼此紧跟,没有间隔。解析此类消息的最简单方法是将字节的输入数组强制转换为适当的类型。问题是流中的数据被压缩,即未对齐 在x86上,这可以通过使用#pragma包(1)来解决。但是,这在

我正在为二进制协议(Javad GRIL协议)编写一个解码器。它由大约一百条消息组成,数据格式如下:

struct MsgData {
    uint8_t num;
    float x, y, z;
    uint8_t elevation;
    ...
};
这些字段是ANSI编码的二进制数,它们彼此紧跟,没有间隔。解析此类消息的最简单方法是将字节的输入数组强制转换为适当的类型。问题是流中的数据被压缩,即未对齐

在x86上,这可以通过使用
#pragma包(1)
来解决。但是,这在其他一些平台上不起作用,或者由于进一步处理未对齐的数据而导致性能开销

另一种方法是为每种消息类型编写特定的解析函数,但正如我所提到的,该协议包含数百条消息

另一种选择是使用类似Perl
unpack()的函数,并将消息格式存储在某处。比如说,我们可以定义MsgDataFormat“CfffC”
,然后调用
unpack(pMsgBody,MsgDataFormat)
。这要短得多,但仍然容易出错和冗余。此外,由于消息可以包含数组,因此格式可能更复杂,因此解析器速度慢且复杂

有没有共同有效的解决办法?我读过书,在谷歌上搜索过,但没有找到更好的方法


也许C++有一个解决方案?

< p>简单的答案是否定的,如果消息是一个特定的二进制格式,它不能简单地被铸造,你别无选择,只能为它编写解析器。如果您有消息描述(比如xml或某种形式的易于解析的描述),为什么不根据该描述自动生成解析代码呢?它不会像cast那么快,但生成消息的速度会比手工编写每条消息快得多。

我解析二进制输入的解决方案是使用Reader类,因此每个消息条目都可以定义读取的内容,并且读取器可以检查溢出、欠载等情况

就你而言:

msg.num = Reader.getChar();
msg.x = Reader.getFloat();
msg.y = Reader.getFloat();
msg.z = Reader.getFloat();
msg.elevation = Reader.getChar();

它仍然需要大量的工作,而且容易出错,但至少它有助于检查错误。

好的,下面是使用VC10和GCC4.5.1()为我编译的代码。我认为C++1x的所有这些需求都是
,在较旧的编译器中也应该可以使用它(如
std::tr1::tuple

它仍然需要您为每个成员键入一些代码,但这是非常少的代码。(见我最后的解释。)

#包括
#包括
typedef无符号字符uint8;
typedef无符号字符字节;
结构MsgData{
uint8_t num;
浮动x;
uint8_t高程;
静态常量std::size\u t buffer\u size=sizeof(uint8\u t)
+sizeof(浮动)
+sizeof(uint8_t);
std::tuple get\u tied\u tuple()
{return std::tie(num,x,elevation);}
std::tuple get\u tied\u tuple()常量
{return std::tie(num,x,elevation);}
};
//仅用于测试输出

内嵌STD::OSWATE和Trase< P>我认为您不可以避免在纯C++中使用每一个消息编写特殊的解析例程(不使用PrimMA)。 <>如果所有的消息都是简单的,POD,类似C的结构,我认为最简单的解决办法是编写代码生成器:把结构放在一个没有其他C++的头文件中,编写一个简单的解析器(使用正则表达式的Perl/Python/BASH脚本就足够了)-或寻找一个-能够在任何消息中找到变量名称;然后使用它为任何消息自动生成一些代码来读取它,如下所示:

YourStreamType & operator>>( YourStreamType &stream, MsgData &msg ) {
    stream >> msg.num >> msg.x >> msg.y >> msg.z >> msg.elevation;
    return stream;
}
将您的流类型
操作符>
专门化为您的消息包含的任何基本类型,您应该执行以下操作:

MsgData msg;
your_stream >> msg;

您始终可以自己调整内存:

uint8_t msg[TOTAL_SIZE_OF_THE_PARTS_OF_MsgData];
由于
sizeof(MsgData)
返回MsgData+填充字节的大小,您可以计算

enum { TOTAL_SIZE_OF_THE_PARTS_OF_MsgData = 
    2*sizeof(uint8_t)+
    3*sizeof(float)+sizeof(THE_OTHER_FIELDS)
}
对此类常量使用枚举是在多台计算机上得到充分验证的概念

将二进制消息读入msg数组。稍后,您可以将这些值强制转换为MsgData值:

unsigned ofs = 0;
MsgData M;
M.num = (uint8_t)(&msg[ofs]);
ofs += sizeof(M.num);
M.x = (float)(&msg[ofs]);
ofs += sizeof(M.x);
等等

如果不喜欢类型转换,请使用memcpy:

memcpy(&M.x,&msg[ofs],sizeof(M.x)); ...

我假设使用元组类型来定义消息,您可以编写函数模板来迭代元组成员,并为您使用的任何类型调用适当的提取函数。但是,我想不出一个自动将这些元组转换为结构的方法。假设您使用的是MSVC++
#pragma pack(1)
,即使在其他平台上也应该可以工作。打包是根据位移位和掩码实现的,而不是操作系统对齐修复。您的数据未打包、未对齐。所以,唯一正确的方法是按字节访问,比如@larsmans建议的
unpack
。@sbi我也搞不懂它)@Billy不幸的是,我正在写QNX和其他异国平台。我已经玩了一段时间,并用一个完整的版本更新了我的答案,这个版本似乎可以满足你的需要。“阅读器类”=
std::istream
std::streambuf
@Billy:就是这样。我已经使用Reader类一段时间了,所以我从未使用过更标准的系统。很好。是的,但这就是我所说的“为每条消息编写特定的解析例程”)+1很好。我喜欢您显示数据读取到每个成员而不是读取整个结构的方式。我还喜欢读者在不需要修改接收结构的情况下处理Endianess的方式。@gaga:当然这是特定于消息的,但是如果您在某个地方定义了消息,比如在头文件中,您可以编写一个脚本,使用该头文件作为输入,为您生成类似于上述内容的内容。使用tuple…的好例子可以提供非常好的语法。C++11岩石。最好是你在ideone.com上提供完整的源代码!
unsigned ofs = 0;
MsgData M;
M.num = (uint8_t)(&msg[ofs]);
ofs += sizeof(M.num);
M.x = (float)(&msg[ofs]);
ofs += sizeof(M.x);
memcpy(&M.x,&msg[ofs],sizeof(M.x)); ...