如何读取特定大小并在c ++中存储未知类型的数据?

时间:2009-02-16 03:09:40

标签: c++ casting

我正在尝试从二进制文件中读取数据,然后存储在数据结构中供以后使用。问题是我不想在我刚读取并存储它时确切地确定它是什么类型。我只想存储有关它是什么类型的数据以及这种特定类型的数据的信息(在该数据的前几个字节中容易获得的信息)

但是我怎样才能读取一定数量的数据,忽略它的类型,并且以后很容易将这些数据转换成可读形式?

我的第一个想法是使用字符,因为我将要查看的所有数据都将以字节为单位。

但如果我做了这样的事情:

ifstream fileStream;
fileStream.open("fileName.tiff", ios::binary);
//if I had to read in 4 bytes of data
char memory[4];
fileStream.read((char *)&memory, 4);

但是如果我以后想读这个并且知道它是双重的话,怎么能投出这4个字节?

读取未知类型数据但知道大小供以后使用的最佳方法是什么? 的FireStream。

5 个答案:

答案 0 :(得分:2)

我认为reinterpret_cast可以满足您的需求。如果您对字节有char *,则可以执行以下操作:

double * x = reinterpret_cast<double *>(dataPtr);

有关reinterpret_cast的更详细说明,请查看Type Casting on cplusplus.com

答案 1 :(得分:1)

您可以将其复制到已知的数据结构中,以便以后的生活更轻松:

double x;
memcpy (&x,memory,sizeof(double));

或者您可以将其称为演员值:

if (*((double*)(memory)) == 4.0) {
    // blah blah blah
}

我相信char*是读取它的最佳方式,因为char的大小保证为1个单位(不一定是一个字节,但所有其他数据类型都是以该单位定义的) ,所以,如果sizeof(double)== 27,你知道它将适合char [27])。所以,如果你有一个已知的大小,这是最简单的方法。

答案 2 :(得分:1)

您可以使用结构和匿名联盟:

struct Variant
{
    size_t size;

    enum
    {
        TYPE_DOUBLE,
        TYPE_INT,
    } type;

    union
    {
        char raw[0];  // Copy to here. *

        double asDouble;
        int asInt;
    };
};

可选:创建一个type =&gt;的表格size,因此您可以在运行时找到给定类型的大小。只有在阅读时才需要这样做。

    static unsigned char typeSizes[2] =
    {
        sizeof(double),
        sizeof(int),
    };

用法:

Variant v;
v.type = Variant::TYPE_DOUBLE;
v.size = Variant::typeSizes[v.type];
fileStream.read(v.raw, v.size);

printf("%f\n", v.asDouble);

您可能会收到有关打字类型的警告。阅读:执行此操作不可移植且违反标准!然后,reinterpret_cast,C风格转换等也是如此。

注意:首先编辑,我没有阅读你原来的问题。我只有工会,而不是尺寸或类型。

*这是我很久以前学到的一个巧妙的技巧。基本上,raw不占用任何字节(因此不会增加并集的大小),而是提供指向联合中位置的指针(在本例中为开头)。它在描述文件结构时非常有用:

struct Bitmap
{
    // Header stuff.
    uint32_t dataSize;

    RGBPixel data[0];
};

然后,您可以fread将数据转换为Bitmap。 =]

答案 3 :(得分:1)

您可以将数据存储在一个类中,该类提供将其强制转换为可能的结果类型的函数,如下所示:

enum data_type {
  TYPE_DOUBLE,
  TYPE_INT
};

class data {
public:
  data_type type;
  size_t len;
  char *buffer;

  data(data_type a_type, char *a_buffer, size_t a_len)
      : type(a_type), buffer(NULL), len(a_len) {
    buffer = new char[a_len];
    memcpy(buffer, a_buffer, a_len);
  }
  ~data() {
    delete[] buffer;
  }

  double as_double() {
    assert(TYPE_DOUBLE == type);
    assert(len >= sizeof(double));
    return *reinterpret_cast<double*>(buffer);
  }

  int as_int() {...}
};

稍后你会做这样的事情:

data d = ...;
switch (d.type) {
case TYPE_DOUBLE:
   something(d.as_double());
   break;
case TYPE_INT:
   something_else(d.as_int());
   break;
...
}

至少我是如何做这些事情的。)

答案 4 :(得分:1)

小心点。在我所知道的大多数环境中,双打是8个字节,而不是4个字节;根据{{​​1}}后面的四个字节包含的内容reinterpret_cast memory memory将导致垃圾邮件。如果你想要一个32位浮点值,你可能想要一个浮点数(虽然我应该注意到C ++标准不要求floatdouble以任何方式表示,特别是不需要符合IEEE-754标准。)

此外,除非您在代码中考虑了字节顺序,否则您的代码将无法移植。我看到TIFF格式的前两个字节中有一个字节序标记,它可以告诉你是在读大端还是小端值。

所以我会用以下原型编写一个函数:

template<typename VALUE_TYPE> VALUE_TYPE convert(char* input);

如果您想要完全可移植性,请专门化模板并让它实际解释input中的位。否则,你可能会逃脱例如。

template<VALUE_TYPE> VALUE_TYPE convert(char* input) {
  return reinterpret_cast<double>(input);
}