2012-12-30 38 views
12

感謝Mats Petersson爲解釋將向量複製到數組似乎工作。這裏的代碼snipset:如何在C++中正確地將矢量寫入二進制文件?

#include <iostream> 
#include <string.h> 
#include <vector> 
#include <fstream> 

using namespace std; 

class Student 
    { 
    private: 
    char m_name[30]; 
    int m_score; 

    public: 
    Student() 
     { 

     } 
    Student(const Student& copy) 
     { 
      m_score = copy.m_score; //wonder why i can use this statment as 
      strncpy(m_name, copy.m_name, 30); //declare it private 
     } 
     Student(const char name[], const int &score) 
     :m_score(score) 
     { 
      strncpy(m_name, name, 30); 
     } 
     void print() const 
     { 
      cout.setf(ios::left); 
      cout.width(20); 
      cout << m_name << " " << m_score << endl; 
     } 
     }; 


     int main() 
     { 
     vector<Student> student; 
     student.push_back(Student("Alex",19)); 
     student.push_back(Student("Maria",20)); 
     student.push_back(Student("muhamed",20)); 
     student.push_back(Student("Jeniffer",20)); 
     student.push_back(Student("Alex",20)); 
     student.push_back(Student("Maria",21)); 
     { 
     Student temp[student.size()]; 
     unsigned int counter; 
     for(counter = 0; counter < student.size(); ++counter) 
     { 
     temp[counter] = student[counter]; 
     } 

     ofstream fout("data.dat", ios::out | ios::binary); 
     fout.write((char*) &temp, sizeof(temp)); 
     fout.close(); 
     } 

     vector<Student> student2; 
     ifstream fin("data.dat", ios::in | ios::binary); 

     { 
     fin.seekg(0, ifstream::end); 
     int size = fin.tellg()/sizeof (Student); 
     Student temp2[size]; 
     fin.seekg(0, ifstream::beg); 
     fin.read((char*)&temp2, sizeof(temp2)); 
     int counter; 
     for(counter = 0; counter <6; ++counter) 
     { 
     student2.push_back(temp2[counter]); 
     } 
     fin.close(); 
     } 
     vector<Student>::iterator itr = student2.begin(); 
     while(itr != student2.end()) 
     { 
     itr->print(); 
     ++itr; 
     } 
     return 0; 
     } 

但我客人這種方法將浪費巨大的內存和繁瑣。也許我會考慮用熊貓和其他建議來寫它先生。 感謝大家的回答。

+3

標準提示:用'g ++ -Wall -g'編譯,改進你的代碼,直到沒有給出警告,學會使用'gdb'和'valgrind'來調試它。 –

+0

以及我還沒有得到與終端編譯技術尚未使用。我想我必須以某種方式學習它。謝謝回覆。 – dchochan

+0

然後,學習使用'make'並編寫簡單的'Makefile' –

回答

8

您正在寫入文件的矢量結構,而不是其數據緩衝區。嘗試更改寫入過程:

ofstream fout("data.dat", ios::out | ios::binary); 
fout.write((char*)&student[0], student.size() * sizeof(Student)); 
fout.close(); 

而不是從文件大小計算矢量的大小,最好在寫入矢量大小(對象數)之前。在這種情況下,你可以寫入同一個文件的其他數據。

size_t size = student.size(); 
fout.write((char*)&size, sizeof(size)); 
+0

你是不是要把'sizeof(size)'放在最後一行? –

3

因爲該模板包含內部指針,所以寫入並重新讀取它們是毫無意義的,因此您可能無法使用二進制(您正在做的方式)任何std::vector

一些一般性的建議:

  • 不二進制寫任何STL模板容器(如std::vectorstd::map),他們肯定包含您真的不想寫的是內部的指針。如果你真的需要寫它們,實現你自己的寫作和閱讀例程(例如使用STL迭代器)。

  • 避免無意中使用strcpy。如果名稱超過30個字符,您的代碼將崩潰。至少,請使用strncpy(m_name, name, sizeof(m_name));(但即使這樣也會對30個字符的名稱起作用)。其實m_name應該是std::string

  • 顯式序列化您的容器類(通過處理每個有意義的成員數據)。您可以考慮使用JSON表示法(或者也許YAML,或者甚至可能是XML--我發現它太複雜了,因此不建議)進行序列化。它爲您提供文本轉儲格式,您可以使用標準編輯器輕鬆檢查(例如emacsgedit)。您會發現很多序列化免費庫,例如jsoncpp等等。

  • 學會用g++ -Wall -g編譯並使用gdb調試器和valgrind內存泄漏檢測器;也學會使用make並編寫你的Makefile-s。

  • 利用Linux是免費軟件,因此您可以查看它的源代碼(即使STL頭文件很複雜,您也可能想學習stdC++實現)。

+0

好的謝謝你的答案,但我可以再問你一次。我曾經在Windows下測試過這段代碼,有時它只是暗戀。 – dchochan

+0

!那又如何?你的程序有**未定義的行爲**。 –

+0

我想你是對的,也許風* ws內存管理和安全性不如linux緊。 – dchochan

2

對於函數read()和write(),您需要什麼叫「普通舊數據」或「POD」。這基本上意味着類或結構必須沒有指針,並且沒有虛函數。矢量的實現當然有指針 - 我不確定虛函數。

你將不得不編寫一個函數來存儲一個學生一次(或者把一堆學生轉換爲一個數組[不是矢量]字節或某些類型 - 但這更復雜)。

不能將非POD數據(尤其是指針)寫入二進制文件的原因是,當您再次讀取數據時,幾乎可以肯定地認爲內存佈局已經從寫它時發生變化。這有點像試圖在商店的同一停車位上停車 - 下次出現時,其他人將從入口處的第三處停車,所以您必須選擇另一個位置。將編譯器分配的內存視爲停車位,將學生信息視爲汽車。

【技術上,更糟糕的是 - 你的矢量實際上並不包含班級內的學生,這是你正在寫的文件,所以你甚至沒有保存學生的信息,只是他們所在的位置(停車位數)的信息]

14

存儲一vector<T>PODs在一個文件中,你必須寫的載體,沒有載體本身的內容。您可以使用&vector[0]訪問原始數據,第一個元素的地址(因爲它至少包含一個元素)。要獲取的原始數據的長度,乘以元素數向量中的一個元件的尺寸:

strm.write(reinterpret_cast<const char*>(&vec[0]), vec.size()*sizeof(T)); 

同樣當從文件中讀取的矢量適用的;元素計數的總文件大小的一個元素 的大小劃分(因爲你只在文件中存儲一個類型的POD):

const size_t count = filesize/sizeof(T); 
std::vector<T> vec(count); 
strm.read(reinterpret_cast<char*>(&vec[0]), count*sizeof(T)); 

這隻能如果你能計算元素的基礎數在文件大小上(如果你只存儲一種類型的POD或者所有向量包含相同數量的元素)。如果具有不同POD的矢量長度不同,則必須在寫入原始數據之前將矢量中元素的數量寫入文件。

此外,當您在不同系統之間以二進制形式傳輸數字類型時,請注意endianness