2009-02-16 46 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); 
}