4

我正在寻找任何在 C++ 中解析二进制 msg 的示例库。大多数人要求读取二进制文件或在套接字中接收到的数据,但我只有一组需要解码的二进制消息。有人提到 boost::spirit,但我无法找到适合我需要的示例。

例如:9A690C12E077033811FFDFFEF07F042C1CE0B704381​​E00B1FEFFF78004A92440

其中前 8 位是前导码,接下来的 6 位是 msg ID(从 0 到 63 的整数),接下来的 212 位是数据,最后 24 位是 CRC24。

所以在这种情况下,味精 26,我必须从 212 个数据位中获取这些数据:

  • 4位整数值
  • 4位整数值
  • 从 0 到 63.875 的 9 位浮点值,其中 LSB 为 0.125
  • 4位整数值

编辑:我需要在位级别操作,所以 memcpy 不是一个好的解决方案,因为它复制了许多字节。要获得第一个 4 位整数值,我应该从一个字节中获得 2 位,从下一个字节中获得另外 2 位,移动每一对并组合。我要求的是一种更优雅的提取值的方式,因为我有大约 20 条不同的消息,并且希望找到一个通用的解决方案来在位级别解析它们。

等等。

你知道任何可以轻松实现这一目标的库吗?

我还发现了其他使用 static_cast 的Q/A 。我用谷歌搜索了它,对于每个推荐这种方法的人,还有另一个关于字节序的警告。由于我已经收到我的消息,我不知道这样的警告是否适用于我,或者只是用于套接字通信。

编辑: boost:dynamic_bitset 看起来很有希望。使用它有什么帮助吗?

4

3 回答 3

6

如果找不到通用库来解析数据,请使用位域获取数据并将其 memcpy() 放入struct. 请参阅链接位域。这将更加简化您的应用程序。

不要忘记打包结构。

例子:

#pragma pack

include "order32.h"
struct yourfields{
#if O32_HOST_ORDER == O32_BIG_ENDIAN
   unsigned int preamble:8;
   unsigned int msgid:6;
   unsigned data:212;
   unsigned crc:24;
#else
   unsigned crc:24;
   unsigned data:212;
   unsigned int msgid:6;
   unsigned int preamble:8;
#endif
}/*__attribute__((packed)) for gcc*/;

如果您的机器使用 LITTLE ENDIAN 或 BIG ENDIAN 格式,您可以做一些编译时检查来断言。之后将其定义为预处理符号::

//order32.h

#ifndef ORDER32_H
#define ORDER32_H

#include <limits.h>
#include <stdint.h>

#if CHAR_BIT != 8
#error "unsupported char size"
#endif

enum
{
    O32_LITTLE_ENDIAN = 0x03020100ul,
    O32_BIG_ENDIAN = 0x00010203ul,
    O32_PDP_ENDIAN = 0x01000302ul
};

static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
    { { 0, 1, 2, 3 } };

#define O32_HOST_ORDER (o32_host_order.value)

#endif

感谢 Christoph @这里的代码

使用位域及其输出的示例程序:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <memory.h>
using namespace std;

struct bitfields{
  unsigned opcode:5;
  unsigned info:3;
}__attribute__((packed));

struct bitfields opcodes;

/* info: 3bits; opcode: 5bits;*/
/* 001 10001  => 0x31*/
/* 010 10010  => 0x52*/

void set_data(unsigned char data)
{
  memcpy(&opcodes,&data,sizeof(data));
}

void print_data()
{
  cout << opcodes.opcode << ' ' << opcodes.info << endl;
}

int main(int argc, char *argv[])
{
  set_data(0x31);
  print_data(); //must print 17 1 on my little-endian machine
  set_data(0x52); 
  print_data(); //must print 18 2
  cout << sizeof(opcodes); //must print 1
  return 0;
}
于 2012-10-22T08:05:28.190 回答
1

您可以自己操作位,例如解析 4 位整数值:

char[64] byte_data;
size_t readPos = 3; //any byte
int value = 0; 
int bits_to_read = 4;
for (size_t i = 0; i < bits_to_read; ++i) {
    value |= static_cast<unsigned char>(_data[readPos]) & ( 255 >> (7-i) );
}

浮点数通常作为字符串数据发送:

std::string temp;
temp.assign(_data+readPos, 9);
flaot value = std::stof(temp);

如果您的数据包含自定义浮点格式,那么只需提取位并进行数学运算:

char[64] byte_data;
size_t readPos = 3; //any byte
float value = 0; 
int i = 0;
int bits_to_read = 9;
while (bits_to_read) {
    if (i > 8) {
      ++readPos;
      i = 0;
    }
    const int bit = static_cast<unsigned char>(_data[readPos]) & ( 255 >> (7-i) );
    //here your code
    ++i;
    --bits_to_read;
}
于 2012-10-22T08:04:51.870 回答
0

这是一篇很好的文章,描述了该问题的几种解决方案。

ibstream它甚至包含对作者专门为此目的创建的类的引用(尽管链接似乎已失效)。我能找到的关于这个类的唯一其他提及是在这里bit的C++ 库中——它可能是你需要的,虽然它不流行并且它在 GPL 下。

无论如何,这boost::dynamic_bitset可能是最好的选择,因为它经过时间考验和社区证明。但我没有个人经验。

于 2012-10-22T08:29:17.183 回答