2016-04-12 5 views
5

私はプログラムを書いて定期的に構造体を格納し、以下の形式で構造体を読み込みます。シリアライゼーションライブラリなしで、C++で構造体からデータを読み書きする最も簡単な方法は何ですか?

struct Node { 
    int leftChild = 0; 
    int rightChild = 0; 
    std::string value; 
    int count = 1; 
    int balanceFactor = 0; 
}; 

ノードをファイルに読み書きするにはどうすればよいですか? seekgとseekpを使ってfstreamクラスを使用してシリアル化を手動で行うのですが、ドキュメントに基づいてどのように動作するのか分かりませんし、まともなサンプルを見つけるのに苦労しています。

[編集]私はシリアル化ライブラリを使いたくないと指定しました。

+1

あなたは、あなたは私が使用したhttp://www.boost.org/doc/libs/1_60_0/libs/serialization/doc/tutorial.html – xiaodong

答えて

3

オブジェクトをシリアル化するには、オブジェクトがそのメンバをストリームに書き込んでおり、ストリームからメンバを読み込んでいるという概念に固執する必要があります。また、メンバオブジェクトは、自身をストリームに書き込む必要があります(読み込みも同様)。

私は3つのメンバ関数を使用してスキーム、及びバッファ実装:size_on_streamバッファオブジェクトのサイズ(またはどのくらいのスペースがバッファ内の占有を決定するために最初に呼び出されることになる

void load_from_buffer(uint8_t * & buffer_pointer); 
void store_to_buffer(uint8_t * & buffer_pointer) const; 
unsigned int size_on_stream() const; 

を)。

load_from_buffer関数は、指定されたポインタを使用してバッファからオブジェクトのメンバーをロードします。この関数は、ポインタを適切にインクリメントします。

store_to_bufferファンクションは、指定されたポインタを使用してオブジェクトのメンバーをバッファに格納します。この関数は、ポインタを適切にインクリメントします。

これは、テンプレートとテンプレートの特殊化を使用してPODタイプに適用できます。

これらの関数を使用すると、出力をバッファにパックし、パック形式からロードすることもできます。

バッファへのI/Oの理由は、あなたがそのようwritereadなど、より効率的なブロックストリームのメソッドを使用することができます。

編集1:ストリーム
にノードを書き込む書き込みに問題またはノードを直列化は、(リンクリストやツリーノード)ポインタがファイルに変換していないということです。 OSがあなたのプログラムを同じメモリ位置に置くか、毎回同じメモリ領域を与えるという保証はありません。

2つのオプションがあります。1)データのみを保存します。 2)ポインタをファイルオフセットに変換する。オプション2)は、ファイルオフセットが事前にわかっていない可能性があるため、ファイルポインタの位置を変更する必要があるため、非常に複雑です。

また、文字列のような可変長レコードにも注意してください。文字列オブジェクトをファイルに直接書き込むことはできません。固定の文字列幅を使用しない限り、文字列サイズは変更されます。文字列の長さに接頭辞を付けるか(好む)、 '\ 0'のような終端文字を使う必要があります。文字列の終わりを検索する必要がないので、最初に文字列の長さが優先されます。テキストを読むためにブロック読み込みを使用することができます。

4

この問題はserializationとして知られています。たとえば、次のようなシリアライズライブラリを使用します。 GoogleのProtocol BuffersまたはFlatbuffers

+0

ブーストからチュートリアルを得ることができますブーストシリアライズライブラリを使用することができます混合成功に過去にシリアル化。 – inetknght

+0

申し訳ありませんが、私の質問は具体的ではありませんでした。私は自分でシリアル化をしたいと思います。 –

0

あなたが参照しているプロセスをシリアル化といいます。私はそれが両方JSONXMLバイナリシリアル化をサポートしており、(良い例で)非常に使いやすいです​​

で穀物をお勧めします。

(残念ながら、それは私のお気に入りのフォーマットYAMLをサポートしていません)

1

あなたはchar型のバッファによってはstd ::文字列を置換する場合は、ディスクにしてから、あなたの構造を読む/書くことfwriteのと関数freadを使用することができます情報の固定されたサイズのブロックとして。一つのプログラムの中でうまくいくはずです。

big bug-a-booは、データを整列させるためにコンパイラがフィールド間にパディングを挿入するという事実です。そのため、モジュールが異なる整列要件でコンパイルされているかのように、コードの移植性が低下します。文字通り構造が異なるサイズになる可能性があり、固定サイズの仮説をドアから投げかけます。

私はある種のシリアライゼーションライブラリによく身に着けています。

1

もう1つのアプローチは、構造体のオペレータ< <と演算子>>をオーバーロードして、それ自身を保存/ロードする方法を知ることです。これは、ノードを読み書きする場所を知る上での問題を軽減します。理論的には、左右の子フィールドは、ノードが実際に存在する場所へのアドレスを求めることができ、新しいフィールドは現在のノードの探索場所を保持することができます。

1

独自のシリアライズ方法を実装する場合、最初に行う必要があるのは、ディスク上のデータをバイナリ形式またはテキスト形式にするかどうかです。

バイナリ形式で保存する方が簡単です。実装に必要な機能の数は少ないです。基本型、コンパイル時に既知のサイズの配列、動的な配列と文字列を書くことができる関数を実装する必要があります。他のすべてはそれらの上に構築することができます。

ここでは、最近私が生産コードに入れたものに非常に近いものがあります。

#include <cstring> 
#include <fstream> 
#include <cstddef> 
#include <stdexcept> 

// Class to write to a stream 
struct Writer 
{ 
    std::ostream& out_; 

    Writer(std::ostream& out) : out_(out) {} 

    // Write the fundamental types 
    template <typename T> 
     void write(T number) 
     { 
     out_.write(reinterpret_cast<char const*>(&number), sizeof(number)); 
     if (!out_) 
     { 
      throw std::runtime_error("Unable to write a number"); 
     } 
     } 

    // Write arrays whose size is known at compile time 
    template <typename T, uint64_t N> 
     void write(T (&array)[N]) 
     { 
     for(uint64_t i = 0; i < N; ++i) 
     { 
      write(array[i]); 
     } 
     } 

    // Write dynamic arrays 
    template <typename T> 
     void write(T array[], uint64_t size) 
     { 
     write(size); 
     for(uint64_t i = 0; i < size; ++i) 
     { 
      write(array[i]); 
     } 
     } 

    // Write strings 
    void write(std::string const& str) 
    { 
     write(str.c_str(), str.size()); 
    } 

    void write(char const* str) 
    { 
     write(str, std::strlen(str)); 
    } 
}; 

// Class to read from a stream 
struct Reader 
{ 
    std::ifstream& in_; 
    Reader(std::ifstream& in) : in_(in) {} 

    template <typename T> 
     void read(T& number) 
     { 
     in_.read(reinterpret_cast<char*>(&number), sizeof(number)); 
     if (!in_) 
     { 
      throw std::runtime_error("Unable to read a number."); 
     } 
     } 

    template <typename T, uint64_t N> 
     void read(T (&array)[N]) 
     { 
     for(uint64_t i = 0; i < N; ++i) 
     { 
      read(array[i]); 
     } 
     } 

    template <typename T> 
     void read(T*& array) 
     { 
     uint64_t size; 
     read(size); 
     array = new T[size]; 
     for(uint64_t i = 0; i < size; ++i) 
     { 
      read(array[i]); 
     } 
     } 

    void read(std::string& str) 
    { 
     char* s; 
     read(s); 
     str = s; 
     delete [] s; 
    } 
}; 

// Test the code. 

#include <iostream> 

void writeData(std::string const& file) 
{ 
    std::ofstream out(file); 
    Writer w(out); 
    w.write(10); 
    w.write(20.f); 
    w.write(200.456); 
    w.write("Test String"); 
} 

void readData(std::string const& file) 
{ 
    std::ifstream in(file); 
    Reader r(in); 

    int i; 
    r.read(i); 
    std::cout << "i: " << i << std::endl; 

    float f; 
    r.read(f); 
    std::cout << "f: " << f << std::endl; 

    double d; 
    r.read(d); 
    std::cout << "d: " << d << std::endl; 

    std::string s; 
    r.read(s); 
    std::cout << "s: " << s << std::endl; 
} 

void testWriteAndRead(std::string const& file) 
{ 
    writeData(file); 
    readData(file); 
} 

int main() 
{ 
    testWriteAndRead("test.bin"); 
    return 0; 
} 

出力:

i: 10 
f: 20 
d: 200.456 
s: Test String 

Nodeを読み書きする能力が非常に簡単に実装されています。

void write(Writer& w, Node const& n) 
{ 
    w.write(n.leftChild); 
    w.write(n.rightChild); 
    w.write(n.value); 
    w.write(n.count); 
    w.write(n.balanceFactor); 
} 

void read(Reader& r, Node& n) 
{ 
    r.read(n.leftChild); 
    r.read(n.rightChild); 
    r.read(n.value); 
    r.read(n.count); 
    r.read(n.balanceFactor); 
} 
関連する問題