29

ベクトルを配列にコピーする方法を説明してくれたMatsPeterssonに感謝します、それはうまくいくようです。コードスニペットは次のとおりです。

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

しかし、私はこの方法は膨大なメモリを浪費し、面倒になると思います。たぶん私はそれをオセロットや他の提案でミスターに書くことを検討します。答えてくれてありがとう。

4

4 に答える 4

25

vector<T> PODをファイルに保存するには、ベクター自体ではなく、ベクターの内容を書き込む必要があります。最初の要素のアドレスを使用して生データにアクセスできます&vector[0](少なくとも1つの要素が含まれている場合)。生データの長さを取得するには、ベクトル内の要素の数に1つの要素のサイズを掛けます。

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

ファイルからベクターを読み取る場合も同じです。要素数は、ファイルの合計サイズを1つの要素のサイズで割ったものです(ファイルに保存するPODのタイプは1つだけです)。

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

これは、ファイルサイズに基づいて要素の数を計算できる場合にのみ機能します(1つのタイプのPODのみを格納する場合、またはすべてのベクトルに同じ数の要素が含まれる場合)。異なる長さの異なるPODを持つベクトルがある場合は、生データを書き込む前に、ベクトル内の要素の数をファイルに書き込む必要があります。

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

于 2012-12-30T09:30:43.717 に答える
24

データバッファではなく、ベクトル構造をファイルに書き込んでいます。書き込み手順を次のように変更してみてください。

 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));
于 2012-12-30T09:27:39.900 に答える
4

テンプレートには内部ポインタが含まれているため、バイナリ(現在の方法)で書き込むことはおそらく不可能でstd::vectorあり、それらを書き込んだり再読み取りしたりしても意味がありません。

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

  • std::vectorSTLテンプレートコンテナ(またはなど)をバイナリで記述しないでください。これらのコンテナstd::mapには、実際にはそのまま書きたくない内部ポインタが含まれているはずです。本当にそれらを書く必要がある場合は、独自の書き込みおよび読み取りルーチンを実装してください(たとえば、STLイテレーターを使用して)。

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

  • コンテナクラスを明示的にシリアル化します(意味のある各メンバーデータを処理することにより)。シリアル化するために、 JSON表記(またはYAML、あるいはXML-私は複雑すぎると思うのでお勧めしません)を使用することを検討できます。それはあなたにテキストダンプフォーマットを与えます、そしてそれはあなたが標準的なエディタ(例えばemacsまたはgedit)で簡単に調べることができました。jsoncppや他の多くのシリアル化された無料のライブラリがたくさんあります。

  • デバッガーとメモリリーク検出器g++ -Wall -gを使用してコンパイルし、使用する方法を学びます。また、 -sの使い方と書き方も学びます。gdbvalgrindmakeMakefile

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

于 2012-12-30T08:17:26.010 に答える
3

関数read()およびwrite()の場合、「プレーンな古いデータ」または「POD」と呼ばれるものが必要です。つまり、基本的に、クラスまたは構造体の内部にポインターがなく、仮想関数があってはなりません。ベクトルの実装には確かにポインタがあります-仮想関数についてはよくわかりません。

一度に学生を格納する関数を作成する必要があります(または、多数の学生をバイトの配列[ベクトルではない]などに変換しますが、それはより複雑です)。

非PODデータ、特にポインタをバイナリファイルに書き込めない理由は、データを再度読み取るときに、メモリレイアウトが書き込んだときから変更されていることはほぼ間違いないからです。お店の同じ駐車スペースに駐車しようとするような感じになります。次回立ち上がったときに、入り口から3番目の場所に誰かが駐車しているので、別の場所を選ぶ必要があります。コンパイラによって割り当てられたメモリを駐車スペースと考え、学生情報を車と考えてください。

[技術的には、この場合はさらに悪いことになります。ベクターには実際にはクラス内の生徒が含まれていません。これはファイルに書き込んでいるものであるため、生徒に関する情報は保存されておらず、情報のみが保存されています。それらがどこにあるか(駐車スペースの数)について]

于 2012-12-30T08:20:53.013 に答える