4

C++ でバイナリ メッセージを解析する例のライブラリを探しています。ほとんどの人は、バイナリ ファイルまたはソケットで受信したデータの読み取りを要求しますが、デコードする必要があるバイナリ メッセージのセットしかありません。誰かが boost::spirit について言及しましたが、私のニーズに合った適切な例を見つけることができませんでした。

例: 9A690C12E077033811FFDFFEF07F042C1CE0B704381​​E00B1FEFFF78004A92440

ここで、最初の 8 ビットはプリアンブル、次の 6 ビットはメッセージ 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. リンクBitfieldsを参照してください。これは、アプリケーションに対してより合理化されます。

構造体をパックすることを忘れないでください。

例:

#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 形式を使用しているかどうかを確認するために、コンパイル時に少しチェックを行うことができます。その後、それを PREPROCESSOR SYMBOL:: に定義します。

//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 @ hereのコードに感謝します

ビットフィールドとその出力を使用するためのサンプル プログラム:

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

float は通常、文字列データとして送信されます。

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

データにカスタム float 形式が含まれている場合は、ビットを抽出して計算を行うだけです。

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 に答える