2009-02-16 119 views
0

我想从二进制文件中读取数据,然后存储在数据结构中供以后使用。问题是,我不想确切知道什么类型的时候,我正在读取和存储它。我只是想存储关于它是什么类型的数据以及这个特定类型的数据有多少的信息(在这个数据的第一对字节中容易获得的信息)如何在C++中读取特定大小和存储未知类型的数据?

但是我怎样才能读取数据的数量,不管它是什么类型,并且仍然能够稍后将数据转换成可读形式(或类似的东西)?

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

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

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, 。

回答

1

你可以把它复制到已知的数据结构,使生活更轻松稍后:

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

,或者你可以只把它称为铸造值:

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

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

+0

sizeof char被定义为1,但这并不意味着1个8位字节。 – 2009-02-16 03:33:57

2

我认为reinterpret_cast会给你你所需要的。为reinterpret_cast更详细的说明

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

退房Type Casting on cplusplus.com:如果你有一个char *的字节你可以做以下。

1

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

struct Variant 
{ 
    size_t size; 

    enum 
    { 
     TYPE_DOUBLE, 
     TYPE_INT, 
    } type; 

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

     double asDouble; 
     int asInt; 
    }; 
}; 

可选:创建类型=>大小的表,所以你可以找到所给出的类型在运行时的大小。这只在阅读时需要。

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。 =]

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; 
... 
} 

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

1

要小心。在大多数我知道的环境中,双打是8个字节,而不是4个; reinterpret_cast ing 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); 
}