2010-11-29 38 views
1

私は、Stack Overflowとファイルにベクトルを書き込む他の多くのサイトについて、いくつかの記事を読んでいます。私は私が働いていると感じていることを実装しましたが、いくつか問題があります。構造体のデータメンバの1つはクラス文字列であり、ベクトルを読み込むとそのデータは失われます。また、最初の反復を書き込んだ後、追加の反復によってmallocエラーが発生します。ベクトルをファイルに保存してから、プログラムが再び起動したときにそれを読み込むために、以下のコードを変更するにはどうすればよいですか?現在、データメンバのみがベクトルであるが、そのベクトルを操作するメソッドを持つクラスのデストラクタに書き込むコンストラクタで、読み込みが行われます。構造体のベクトルを読み込んでファイルに書き込む

ここに私の読み書き方法の要点があります。

ifstream infile; 
infile.open("data.dat", ios::in | ios::binary); 
infile.seekg (0, ios::end); 
elements.resize(infile.tellg()/sizeof(element)); 
infile.seekg (0, ios::beg); 
infile.read((char *) &elements[0], elements.capacity()*sizeof(element)); 
infile.close(); 

書き込み:

ofstream outfile; 
outfile.open("data.dat", ios::out | ios::binary | ios_base::trunc); 
elements.resize(elements.size()); 
outfile.write((char *) &elements[0], elements.size() * sizeof(element)); 
outfile.close(); 

構造体要素:C++で

struct element { 
int id; 
string test; 
int other;   
}; 
+0

どのようなC++学習教材を使って、このようにファイルへのアクセスを教えてくれましたか? –

+0

これは宿題の一部で、ベクトルデータをファイルに読み書きするためにfstreamを使用する必要があります。クラス内のファイルに複雑なベクトルの書き込みを行うことは正確にはカバーしていなかったので、私はfstreamのドキュメントなどを読んで、例をオンラインで見ました。私はバイナリで書くことがこれを達成する最も簡単な方法だと決めました。サイズ変更のような特定の仕様は、ファイルサイズを制限するために選択したものでした。私たちには公式の本や教材はありません。私たちはいくつかのオンラインノートを持っていますが、私が必要とするものを実装するのに必要なほど細かくはありません。 –

+0

私は文字列を文字配列に変更し、その変更をサポートするためにコード内のいくつかの小さな変更を行うことにしました。文字列ではなく文字配列に切り替えると、上記のコードはうまくいくように見えます。 –

答えて

6

、メモリは一般的に直接読み書きすることはできません読むvector<element> elements ...

を想定すると、そのようなディスクを直接。特に、struct elementにはstringが含まれています。このデータ型は非PODなので、直接アクセスすることはできません。

これを明らかにするには思考実験が役立つかもしれません。あなたのコードでは、すべてのelementの値が同じサイズであることが前提です。 string testの値の1つがあなたが想定していた値よりも長い場合はどうなりますか?ディスクに読み書きするときに使用するコードのサイズはどのように分かりますか?

これを処理する方法の詳細については、serializationをお読みください。

+0

そして、異なるアーキテクチャー(エンディアン)のマシンでこれらのファイルを読み書きする場合は、バイナリをエンディアンニュートラルで書き込み/読み込みして移植できるようにする必要があります。 – David

+0

これは意味があります。私は簡単な解決策は、単に文字列に切り替えることだと思います。クラス文字列は私の好みに合っており、より柔軟に対応できます。 –

1

あなたは、関連するすべてのデータがベクトルの中に直接存在すると仮定しますが、文字列は可変サイズのコンテンツをヒープに追加できるポインタを持つ固定サイズのオブジェクトです。あなたは基本的にテキストではなくポインタを保存しています。たとえば、次のような文字列のシリアライゼーションコードを記述する必要があります。

bool write_string(std::ostream& os, const std::string& s) 
{ 
    size_t n = s.size(); 
    return os.write(n, sizeof n) && os.write(s.data(), n); 
} 

次に、構造体のシリアル化ルーチンを記述できます。いくつかの設計オプションがあります: - 多くの人々は、STDを収容することができますBinary_IStream/Binary_OStreamタイプ:: ostreamに宣言したいが、特殊タイプアラシリアライズ・ルーチンの別のセットを作成するために使用することができますされている。

operator<<(Binary_OStream& os, const Some_Class&); 

または、バイナリシリアル化を扱うときに通常のストリーミング表記を放棄して、代わりに関数呼び出し表記を使用することができます。明らかに、同じコードがバイナリシリアル化と人間が判読可能なシリアライゼーションの両方を正しく出力できるようにするのは良いことです。オペレータベースのアプローチは魅力的です。

数字をシリアル化する場合は、バイナリ形式かASCIIかを決める必要があります。同じOS上で32ビットと64ビットのコンパイル間でも移植性が要求される純粋なバイナリ形式では、型サイズのメタデータ(int32_tやint64_tなど)をエンコードして使用する必要があります(ネットワークバイトオーダーとntohl() - ファミリーファンクションを考慮する)エンディアンとして。 ASCIIではこれらの考慮事項のいくつかを避けることができますが、可変長であり、書き込み/読み込みが遅くなる可能性があります。以下、私は任意にASCIIを '|'番号のターミネータ。

bool write_element(std::ostream& os, const element& e) 
{ 
    return (os << e.id << '|') && write_string(os, e.test) && (os << e.other << '|'); 
} 

そして、あなたのためのベクトル:

os << elements.size() << '|'; 
for (std::vector<element>::const_iterator i = elements.begin(); 
    i != elements.end(); ++i) 
    write_element(os, *i); 

この背中を読むために:

std::vector<element> elements; 
size_t n; 
if (is >> n) 
    for (int i = 0; i < n; ++i) 
    { 
     element e; 
     if (!read_element(is, e)) 
      return false; // fail 
     elements.push_back(e); 
    } 

... ...

bool read_element(std::istream& is, element& e) 
{ 
    char c; 
    return (is >> e.id >> c) && c == '|' && 
      read_string(is, e.test) && 
      (is >> e.other >> c) && c == '|'; 
} 

を必要とします... ...

bool read_string(std::istream& is, std::string& s) 
{ 
    size_t n; 
    char c; 
    if ((is >> n >> c) && c == '|') 
    { 
     s.resize(n); 
     return is.read(s.data(), n); 
    } 
    return false; 
} 
関連する問題