2013-07-26 33 views
1

我想將文件讀入結構體或類中,但是在閱讀完一些文字後我發現它不是一個好主意:從結構體/類讀取/寫入文件

int MyClass::loadFile(const char *filePath) { 

      ifstream file (filePath, ios::in | ios::binary); 

      file.read ((char*)this, 18); 

      file.close(); 

      return 0; 

     } 

我猜如果我想從一個struct /類寫入一個文件,這不是猶太之一:

void MyClass::writeFile(string fileName) { 

     ofstream file(fileName, ofstream::binary); 

     file.write((char*)this, 18); 

     file.close(); 

    } 

這聽起來像我不希望這樣做的原因是因爲,即使如果我的結構的數據成員最多可以添加18個字節,其中一些可能會在內存中填充額外的字節。有沒有更正確/優雅的方式來獲取文件到這樣的類/結構?

+0

唯一真正的解決辦法是寫各個字段逐個。思維排序,甚至更好的是,在寫入之前,將每個字段轉換爲人類可讀的表示(例如,數字42會生成字符串「42」),以便可以在標準文本編輯器中手動編輯您的文件。 – syam

回答

2

首選的通用技術稱爲序列化。

它比二進制表示更脆弱。但它有需要解釋的開銷。標準類型在序列化方面效果很好,我們鼓勵您將您的類序列化,以便包含類的類可以輕鬆地序列化。

class MyClass { 
    int x; 
    float y; 
    double z; 
    friend std::ostream& operator<<(std::ostream& s, MyClass const& data); 
    friend std::istream& operator>>(std::istream& s, MyClass& data); 
}; 

std::ostream& operator<<(std::ostream& s, MyClass const& data) 
{ 
    // Something like this 
    // Be careful with strings (the input>> and output << are not symmetric unlike other types) 
    return str << data.x << " " << data.y << " " << data.z << " "; 
} 

// The read should be able to read the version printed using << 
std::istream& operator>>(std::istream& s, MyClass& data) 
{ 
    // Something like this 
    // Be careful with strings. 
    return str >> data.x >> data.y >> data.z; 
} 

用法:

int main() 
{ 
    MyClass plop; 
    std::cout << plop; // write to a file 
    std::cin >> plop; // read from a file. 


    std::vector<MyClass> data; 

    // Read a file with multiple objects into a vector. 
    std::ifstream loadFrom("plop"); 
    std::copy(std::istream_iterator<MyClass>(loadFrom), std::istream_iterator<MyClass>(), 
       std::back_inserter(data) 
      ); 


    // Write a vector of objects to a file. 
    std::ofstream saveTo("Plip"); 
    std::copy(data.begin(), data.end(), std::ostream_iterator<MyClass>(saveTo)); 

    // Note: The stream iterators (std::istream_iterator) and (std::ostream_iterator) 
    //  are templatized on your type. They use the stream operators (operator>>) 
    //  and (operator<<) to read from the stream. 
} 
+0

謝謝,我真的很感激。我已經看到序列化在我的搜索中彈出了很多,但總是有人推薦boost庫。我真的很討厭不知道到底發生了什麼,所以我直到我更好地理解C++時才避免使用其他庫。我想現在我會仔細看看這段代碼和一些關於序列化的教程。再次感謝! –

+0

我想我應該在我的文章中提到過這一點,但我正在處理位圖和波形文件。這會改變你的答案還是序列化仍然是一條路? –

+0

@RobbyAllsopp:如果你有特定的文件格式;那麼你需要遵循這些格式的規範。您不能假定編譯器使用的內存佈局將完全匹配文件格式。所以你通常會特別寫每個元素,這通常意味着你必須編寫特定的二進制大小(不是sizeof(int))。您可能需要確保像整數這樣的東西是網絡字節順序(請參見規格確定),請參閱[htonl](http://linux.die.net/man/3/htonl)和family。 –

-1

嗯......這可以是好的,偶爾。但是如果你不知道自己在做什麼,或者有人進來想修改某些東西,那就很危險。一次最多可以讀取/寫入您的個人成員變量。

file.read(&m_fMyFloat, sizeof(float)); 
file.read(&m_nMyIntA, sizeof(int)); 
file.read(&m_nMyIntB, sizeof(int)); 

等等,等等......

填充可以包括在內,是的......但它確實不凡有變量本身之間的填充。更常見的問題是,如果您的類來自另一個類,那麼您將在該類的虛擬表上進行讀/寫操作。這是一個非常糟糕的,有點鬼鬼祟祟的問題!你應該避免在this指針上使用任何ZeroMemory方法。

因此,基本上,只要用閱讀的標準模式/寫你的個人成員變量。對於您要覆蓋的案例來說更安全。

如果你真的,真的,真的要在單塊讀...使用分配的內存塊,或一個固定大小的數組作爲類的成員,和讀/寫。然後設置一個非常基本的結構,它表示該內存塊中變量的佈局,並使用該類型的指針來讀取或修改其包含的內容。

+0

而不是危險。我會用「易碎」這個詞。而且,你所建議的技術與C++不能保證的類型信息一樣脆弱。字節序/尺寸/表示。 –

+0

嗯,是的......它很容易斷裂。但是從某種意義上說,你也可能會寫下重要的內存,導致各種隨機不良情況發生,並且可能會在應用程序中創建安全漏洞......或導致數據損壞。無論如何,這是不好的。 – Mattingly

+0

@LokiAstari它不會假設任何東西......它只是不是這個問題中涉及的主題。在閱讀後會根據需要調整字節序大小,字體大小將根據類型進行驗證,如果需要,可能會將大小寫爲序列化值之一。這不是他問題的一部分......所以這不是答案的一部分。 – Mattingly

0

答案是:沒有銀彈這個問題。您可以消除填充

的一種方式,以確保您的類的數據成員是使用(在MSVC你正在使用)

#pragma pack(push, 1) 

class YourClass { 
    // your data members here 
    int Data1; 
    char Data2; 
    // etc... 
}; 

#pragma pack(pop) 

這種方法的主要用處是,如果你的類相匹配的預定義的格式,如位圖標題。如果它是代表貓,狗的通用類,那麼不要使用這種方法。如果這樣做的另一件事情是要確保你知道的數據類型爲你的編譯器的字節長度,如果你的代碼是有史以來將是多平臺的,那麼你應該使用明確的尺寸成員如__int32等

如果這是一個普通類,那麼在您的保存成員中,每個值都應該明確寫入。要做到這一點的提示是創建或從sourceforge獲取或從某處獲得良好的代碼來幫助完成此操作。理想的情況是,一些代碼,允許被命名爲成員,我使用類似的東西:

SET_WRITE_DOUBLE(L"NameOfThing", DoubleMemberOfClass); 
SET_WRITE_INT(L"NameOfThing2", IntMemberOfClass); 
// and so on... 

我創建這些宏,我不共享的,但現在一個聰明的人可以創建自己的代碼後面的代碼保存命名爲無序集中的流。我發現這是一種完美的方法,因爲如果您將數據成員添加到類中或將其減少,則保存/加載不依賴於二進制表示和保存順序,因爲如果按順序保存此類,您的課程無疑會隨時間演化是您將要面對的問題。

我希望這會有所幫助。