3

後で取得できるように、できるだけ小さなファイルに保存したいオブジェクトのリストがあります。私はこのチュートリアルを注意深く読んでおり、理解し始めています (と思います) が、いくつか質問があります。ここに私が取り組んでいるスニペットがあります:

    static bool writeHistory(string fileName)
{
    fstream historyFile;
    historyFile.open(fileName.c_str(), ios::binary);
    if (historyFile.good())
    {
        list<Referral>::iterator i;
        for(i = AllReferrals.begin(); 
                i != AllReferrals.end();
                i++)
        {
            historyFile.write((char*)&(*i),sizeof(Referral));
        }
        return true;
    } else return false;
}

今、これはスニペットから適応されています

file.write((char*)&object,sizeof(className));

チュートリアルから取得。今私が信じているのは、オブジェクトをポインターに変換し、値とサイズを取得してファイルに書き込むことです。しかし、これを行っているのであれば、わざわざ変換を行う必要はありません。最初から値をとらないのはなぜですか?そして、なぜサイズが必要なのですか?さらに、私の理解では、なぜ

historyFile.write((char*)i,sizeof(Referral));

コンパイルしない?i は反復子です (反復子はポインタではありませんか?)。または単に

historyFile.write(i,sizeof(Referral));

とにかくアドレスをいじる必要があるのはなぜですか? データをファイルに保存していませんか? アドレス/値が独自に保持されている場合、プレーンテキストで区切られたアドレスを保存して、後で値を取得できないのはなぜですか?

また、.txt 拡張子を引き続き使用する必要がありますか? <編集>代わりに何を使用すればよいですか? .dtb を試しましたが、ファイルを作成できませんでした。< /edit> ios::binary フラグを使用すると、実際にはエラーなしでファイルを開くことさえできないようです。ファイル名を渡すのにも問題があります(c_str()によって変換された文字列クラス文字列として、コンパイルされますがエラーが発生します)。

些細な質問が多くて申し訳ありませんが、基本的には、オブジェクトをファイルに効率的に格納する方法に要約されますか?

4

6 に答える 6

7

あなたがやろうとしていることはシリアライゼーションと呼ばれます。Boost には、これを行うための非常に優れたライブラリがあります。

あなたがやろうとしていることは、場合によっては、いくつかの非常に重要な条件で機能します。PODタイプでのみ機能します。同じバージョンのコンパイラと同じ引数でコンパイルされたコードに対してのみ機能することが保証されています。

(char*)&(*i)

イテレータ i を取得し、それを逆参照してオブジェクトを取得し、そのアドレスを取得して文字の配列として扱うように指示します。これは、ファイルに書き込まれているものの開始です。 sizeof(Referral)書き出されるバイト数です。

いいえ、イテレーターは必ずしもポインターではありませんが、ポインターはイテレーターのすべての要件を満たしています。

于 2009-09-28T18:59:43.653 に答える
2

イテレータはポインタではありませんか?

イテレータは、外部からのポインタのように機能するものです。ほとんどの(おそらくすべての)場合、それは実際には裸のポインタではなく、何らかの形式のオブジェクトです。イテレータには、ジョブの実行に使用する内部メンバー変数としてポインタが含まれている場合がありますが、必要に応じて、他の何かまたは追加の変数が含まれている場合もあります。

さらに、イテレータの内部に単純なポインタがある場合でも、関心のあるオブジェクトを直接指しているとは限りません。コンテナクラスで使用されるある種の簿記コンポーネントを指している可能性があります。関心のある実際のオブジェクト。幸い、これらの内部の詳細が実際に何であるかを気にする必要はありません。

それを念頭に置いて、ここで何が起こっているのかを説明し(char*)&(*i)ます。

  • *iリストに格納されているオブジェクトへの参照を返します。
  • &そのオブジェクトのアドレスを取得し、オブジェクトへのポインタを生成します。
  • (char*)そのオブジェクトポインタをcharポインタにキャストします。

そのコードスニペットは、次のようなことを行うための短い形式になります。

Referral& r = *i;
Referral* pr = &r;
char* pc = (char*)pr;

とにかくアドレスをいじる必要があるのはなぜですか?

そして、なぜそれはサイズが必要なのですか?

fstream::write一連のバイトをファイルに書き込むように設計されています。それらのバイトが何を意味するかについては何も知りません。アドレスを指定して、そのアドレスが指す場所から開始して存在するバイトを書き込めるようにします。書き込むバイト数がわかるようなサイズを指定します。

だから私がそうするなら:

MyClass ExampleObject;
file.write((char*)ExampleObject, sizeof(ExampleObject));

次に、ファイル内に直接存在するすべてのバイトを書き込みExampleObjectます。

注:他の人が述べているように、書き込みたいオブジェクトに、動的にメモリを割り当てるか、ポインターを使用するメンバーがある場合、メモリを指すものは1回の単純なfstream::write呼び出しでは書き込まれません。


シリアル化により、ストレージ効率が大幅に向上しますか?

理論的には、バイナリデータはプレーンテキストよりも小さく、読み取りと書き込みが高速であることがよくあります。実際には、非常に大量のデータを処理していない限り、違いに気付くことはおそらくないでしょう。最近のハードドライブは大きく、プロセッサは高速です。

そして、考慮すべきことは効率だけではありません。

  • バイナリデータは、必要に応じて調査、デバッグ、および変更するのが困難です。少なくとも追加のツールがなければ、それでもプレーンテキストの方が通常は簡単です。
  • プログラムの異なるバージョン間でデータファイルが保持される場合、オブジェクトのレイアウトを変更する必要がある場合はどうなりますか?バージョン2のプログラムがバージョン1のファイル内のオブジェクトを読み取れるようにコードを書くのはイライラするかもしれません。さらに、事前にアクションを実行しない限り(ファイルにバージョン番号を書き込むなど)、バージョン2のファイルを読み取るバージョン1のプログラムで重大な問題が発生する可能性があります。
  • データを検証する必要がありますか?たとえば、破損や悪意のある変更に対して。このようなバイナリスキームでは、追加のコードを記述する必要があります。一方、プレーンテキストを使用する場合、変換ルーチンは検証の役割を満たすのに役立つことがよくあります。

もちろん、優れたシリアル化ライブラリは、これらの問題のいくつかに役立ちます。また、優れたプレーンテキスト形式のライブラリ(たとえば、XML用のライブラリ)も可能です。まだ学習している場合は、両方の方法を試して、それらがどのように機能するか、および目的に最適な方法を理解することをお勧めします。

于 2009-09-29T00:16:28.490 に答える
2

質問 #1 なぜ ... がコンパイルされないのですか? 回答: i は Referral* ではないため、list::iterator ;; です。イテレータはポインタの抽象化ですが、ポインタではありません。

質問 #2 .txt 拡張子を引き続き使用する必要がありますか? 答え: おそらく違います。.txt は、多くのシステムで MIME タイプ text/plain に関連付けられています。

素朴な質問: これは機能しますか? 回答: 参照にポインタがある場合は、いいえファイルから参照を読み取ろうとすると、ポインターは何かが存在 して たメモリ上の場所を指しますが、少なくともポインターが指していたすべてのものが有効であるという保証はありませんもともとに。気をつけて。

于 2009-09-28T18:58:56.720 に答える
1

あなたがやろうとしていること(ファイルへの/からの生メモリの読み取りと書き込み)は、未定義の動作を引き起こし、プレーンオールドデータ型ではないものに対して壊れ、生成されるファイルはプラットフォームに依存し、コンパイラコンパイラの設定に依存しており、おそらく依存している可能性さえあります。

C++ には、複雑なデータをシリアル化する組み込みの方法がありません。ただし、便利なライブラリがあります。例えば:

http://www.boost.org/doc/libs/1_40_0/libs/serialization/doc/index.html

于 2009-09-28T18:57:51.287 に答える
1

すでにboost::serializationを見たことがありますか。堅牢で、優れたドキュメントがあり、バージョン管理をサポートしています。バイナリ形式ではなく XML 形式に切り替えたい場合は、簡単です。

于 2009-09-28T19:00:01.563 に答える
0

Fstream.write は、生データをファイルに書き込むだけです。最初のパラメーターは、データの開始アドレスへのポインターです。2 番目のパラメーターはオブジェクトの長さ (バイト単位) であるため、write は書き込むバイト数を認識します。

file.write((char*)&object,sizeof(className));

^ この行は、オブジェクトのアドレスをchar ポインターに変換しています。

historyFile.write((char*)i,sizeof(Referral));

^ この行は、オブジェクト (i) を char ポインターに変換しようとしています (無効)

historyFile.write(i,sizeof(Referral));

^ この行は、char ポインターを想定しているときに、書き込みオブジェクトを渡しています。

于 2009-09-28T18:57:14.383 に答える