2012-12-30 37 views
12

マット・ピーターソンのおかげで配列にベクトルをコピーする説明がうまくいくようです。ここでコードスニペット:正しくベクトルをバイナリファイルに書き込む方法は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; 
     } 

しかし、このメソッドは、巨大なメモリと厄介なものを無駄にします。おそらく私はocelotと他の提案とミスターを書くことを検討します。 お返事いただきありがとうございます。

+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(); 

、代わりにベクトルの計算の大きさの、それは前より良い書き込みベクトルの大きさ(オブジェクトの数)です。この場合、同じファイルに他のデータを書き込むことができます。ファイルにPODsvector<T>を保存するために

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

最後の行に 'sizeof(size)'を入れることを意味しましたか? –

3

std::vectorバイナリ(あなたのやり方)で書き込むことはできません。そのテンプレートには内部ポインタが含まれているため、書き込みと再読み込みは意味がありません。

いくつかの一般的なアドバイス:

  • がバイナリで任意のSTLテンプレートコンテナ(のようなstd::vectorまたはstd::map)を書いていない、彼らは確かにあなたが本当にあるとして書きたくない内部ポインタを含みます。実際に記述する必要がある場合は、独自の書き込みルーチンと読み取りルーチンを実装します(STLイテレータなど)。

  • strcpyを気にせずに使用しないでください。名前が30文字を超えると、コードがクラッシュします。少なくとも、strncpy(m_name, name, sizeof(m_name));を使用してください(ただし、30文字の名前ではうまく動作しません)。実際には、m_namestd::stringである必要があります。

  • (意味のあるメンバーデータをそれぞれ処理することによって)明示的にコンテナクラスをシリアライズします。あなたはJSON表記(またはおそらくYAML、または多分XMLであっても複雑すぎるので推奨しません)を使って直列化することを検討することができます。標準のエディタ(例:emacsまたはgedit)で簡単に検査できるテキスト形式のダンプ形式を提供します。あなたは多くのシリアライズフリーライブラリを見つけるでしょう。 jsoncppなどがあります。

  • g++ -Wall -gでコンパイルし、gdbデバッガとメモリリーク検出器valgrindを使用する方法を学びます。 makeを使用し、Makefile -sを書くことも学びます。

  • Linuxはフリーソフトウェアであるため、ソースコードを調べることができます(また、STLヘッダーが複雑な場合でもstdC++実装を検討することができます)。

+0

答えに感謝しますが、私はあなたにもう一度お尋ねすることができます。私はWindowsでこのコードをテストしていましたが、何回か動作していました。 – dchochan

+0

!だから何?あなたのプログラムは**未定義の動作**を持っています。 –

+0

あなたは正しいかもしれないと思うかもしれませんが、Linuxのような厳しいものではないかもしれません。 – dchochan

2

関数read()およびwrite()では、「plain old data」または「POD」と呼ばれるものが必要です。基本的には、クラスや構造体にはポインタがなくてはならず、仮想関数はありません。ベクトルの実装には確かにポインタがあります。仮想関数についてはわかりません。

一度に生徒を保存する関数を書く必要があります(つまり、たくさんの生徒を配列の[ベクトルではなくベクトルなどに変換しますが、それはより複雑です)。

POD以外のデータ、特にポインタをバイナリファイルに書き込めないのは、データをもう一度読み込んだときに、書き込んだときからメモリのレイアウトが変わったと確信することができるからです。それは店の同じ駐車スペースに駐車しようとするようなものになります - 次回に出るときに誰かが入り口から3番目の場所に駐車するので、別の場所を選ぶ必要があります。コンパイラーが駐車スペースとして割り当てたメモリーと、学生情報を車として考える。

[技術的には、この場合、それはさらに悪くなります。実際にベクトルにはクラス内の生徒が含まれていません。これはファイルに書き込むものなので、生徒の情報も保存していません(駐車スペースの数)]

14

、あなたはベクトルではなく、ベクター自体の内容を記述する必要があります。最初の要素のアドレス(少なくとも1つの要素が含まれている場合)の&vector[0]で生データにアクセスできます。生のデータ長を取得するには、一つの要素の大きさとベクトルの要素数を掛ける:あなたは、ファイルからのベクトルを読んだとき

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

同じことが当てはまります。あなたがベースの要素の数を計算することができるかどう

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

のみ機能:要素数が一つの要素 の大きさで割った合計ファイルサイズです(あなたが唯一のファイルにPODのいずれかのタイプを保存することを与えられました) 1つのタイプのPODのみを格納する場合、またはすべてのベクトルに同じ数の要素が含まれている場合は、ファイルサイズを変更します。異なる長さの異なるPODを持つベクトルがある場合、生データを書き込む前にベクトルの要素数をファイルに書き込む必要があります。

さらに、数値型を異なるシステム間でバイナリ形式で転送する場合は、endiannessに注意してください。

関連する問題