web-dev-qa-db-ja.com

C ++でバイナリファイルにベクトルを正しく書き込む方法は?

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

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

しかし、私はこの方法をゲストとして使用すると、膨大なメモリを浪費し、扱いにくくなります。多分私はオセロット氏と他の提案でミスター氏を書くことを検討します。答えてくれてありがとう。

19
dchochan

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

 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));
10
ocelot

vector<T>of PODsをファイルに保存するには、ベクターの内容を書き込む必要があります。ベクトル自体。最初の要素のアドレス&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が異なるベクターがある場合、生データを書き込む前に、ベクターの要素数をファイルに書き込む必要があります。

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

19

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

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

  • バイナリでSTLテンプレートコンテナー(std::vectorstd::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 ++の実装を検討する必要がある場合があります)。

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

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

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

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

3
Mats Petersson