如何在对象中表示不规则但有序的数据?

时间:2014-12-31 16:38:36

标签: c++ binary-data

我有一组包含二进制数据的文件。每个文件由块组成,每个块都有一个标题,然后是一组事件。每个事件都有一个标题,然后是一系列字段。我的问题在于字段序列。

这些字段包含不同长度的有序/结构化数据,但字段不按任何特定顺序排列。例如,在一个事件中,我可能在一个事件中有3个字段,如下所示:

Event Header (12 bytes, always, made of things like number of fields, size, etc)
Field Header (2 bytes, always, field type in the top 4 bits, size in the bottom 12)
Field Data   (4299-4298(VDC) data, Signals from various wires in a vertical drift chamber)
Field Header ( '' )
Field Data   (ADC-LAS data, Signals from various photo multiplier tubes)
Field Header ( '' )
Field Data   (FERA data, Signals from a fast encoding readout adc system)

在另一个事件中,我可能有相同的字段加上一些字段,或者删除了一个字段,另外添加了一个字段等等。这一切都取决于在读出系统触发时哪些DAQ硬件要记录数据。 / p>

我已经考虑了一些可能的解决方案,老实说,这些解决方案对我来说都不合适。

方法1:
创建一个抽象基类Field,然后从中继承每个字段类型(只有13个) 优点:从文件中读取数据很简单,只需获取区域ID,分配适当类型的字段,读取数据并存储Field*。而且,这种方法吸引了我对所有事物和所有事物的位置的感觉 缺点:当我处理事件中的字段以将数据转换为分析实际使用的信息时,我不断需要dynamic_cast<>()所有内容到派生类。这有点乏味和丑陋,我记得在某处(前一段时间)阅读,如果你不得不使用dynamic_cast<>()那么你使用多态“错误”。这也使得字段的对象池变得棘手,因为我需要为Field的每个子类建立一个池。最后,如果稍后添加更多字段类型,那么除了修改处理代码之外,还需要创建其他字段子类。

方法2
只需要一个大的字节数组来存储所有字段标题和数据。然后将其留待处理代码来提取结构以及处理数据 优点:这意味着如果数据格式在将来发生变化,那么需要进行的唯一更改就是在事件处理代码中。这是一个非常简单的解决方案。它更灵活。
缺点:处理/阅读代码中的作业分区较少。感觉不那么优雅。

我认识到可能没有一种解决方案在各方面都会“优雅”,从KISS的角度来看,我倾向于方法2. 我应该选择方法1,方法2,还是是否有一些我没想过的方法3?

1 个答案:

答案 0 :(得分:1)

您正在尝试在struct或tuple或MSRA safeprotocole handler

之间进行选择

`//示例程序     #包括     #包括     #包括     #include

// start ABI Protocole signature
const int EVENT_HEADER_SZ = 12;
const int FIELD_HEADER_SZ = 2;
const int FIELD_DATA_SIZE = 2^12;
// end ABI Protocole
#ifdef WINDOWS
#define __NOVT __declspec(novtable
#else
#define __NOVT
#endif 

struct Protocole_Header __NOVT {
    union {
       char pbody[EVENT_HEADER_SZ+1];
       unsigned ptype    : 32;
       unsigned psize    : 32;
       unsigned pmisc    : 32;
    };
};
struct Field_Header __NOVT {
    union {
        char    fbody[FIELD_HEADER_SZ+1];
        unsigned ftype   : 4;   // type of data 0...15
        unsigned fsize   : 12;  // size of field data to follow up 0..4096 max size
    };
};
struct Field_Data {
    std::string _content;
};

typedef std::tuple<uint_fast32_t, int_fast32_t,uint_fast32_t> Protocole_Header2;
enum PHR{
    TPL_TYPE,
    TPL_SIZE,
    TPL_ETC
};

std::istream& operator >>(std::istream &is, std::tuple<uint_fast32_t, int_fast32_t,uint_fast32_t>& tpl)
{
   is >> std::get<TPL_TYPE>(tpl) >> std::get<TPL_SIZE>(tpl) >> std::get<TPL_ETC>(tpl); 
    return is;
}


union Field_Header2 {
    char    fbody[FIELD_HEADER_SZ];
    unsigned ftype   : 4;   // type of data 0...15
    unsigned fsize   : 12;  // size of field data to follow up 0..4096 max size
};


int main()
{
  Protocole_Header ph;
  Field_Header fh;
  Field_Data fd;

  long i;
  char protocole_buffer[FIELD_DATA_SIZE+1];

  std::cin.get(ph.pbody,EVENT_HEADER_SZ);
  std::cin.get(fh.fbody,FIELD_HEADER_SZ);
  for(i=0;i<ph.psize;++i) 
  {
    std::cin.get(protocole_buffer,fh.fsize); 
    fd._content = protocole_buffer; // push somewhere else 
    std::cin.get(fh.fbody,FIELD_HEADER_SZ);
  }
// ... 


// ...
    Protocole_Header2 ph2;
    Field_Header2 fh2;
    std::cin >> ph2;
    std::cin.get(fh2.fbody,FIELD_HEADER_SZ);
    for(i=0;i<std::get<TPL_SIZE>(ph2);++i) 
    {
        std::cin.get(protocole_buffer,fh.fsize); 
        fd._content = protocole_buffer; // push somewhere else 
        std::cin.get(fh2.fbody,FIELD_HEADER_SZ);
    }
}`

在这里,你有两个答案......

注意,在结构上使用元结构与找回代码并在原型破裂的情况下重新编译代码一样多。 通常你没有为protocole结构定义ABI,这就是C ++ Spirit的原因。

必须使用解析器来处理protocole(总是,因为protocole本身就是一个语法,定义了一个EBNF,你的代码将运行数十年而无需人们重新编译它......)

只有在您需要通过MSRA或Heatlh Care或任何受监管的扇区时,才会使用解析器。其余的时间,不要用C或C ++将外部数据绑定到ABI结构,它是导致错误的100%原因。