Mehrere Instanzen einer Klasse abspeichern



  • Hallo Zusammen,

    Ich möchte Instanzen einer Klasse in einer Datei abspeichern.

    Dazu verwende ich:

    ofstream f("a", ios::binary);
    f.write((char *)element_1, sizeof(*element_1));
    f.close();
    

    und zum lesen:

    element t;
    ifstream ifs("a", ios::binary);
    ifs.read((char *)&t, sizeof(t));
    

    Funktioniert soweit. Nun möchte ich aber nicht nur ein, sondern mehrere Instanzen der Klasse element hintereinander abspeichern.
    Ich vermute zu wissen wie das geht, kann es aber nicht prüfen, da ich die datei "a" nicht öffnen kann, da mein Computer keinen entsprechenden Editor findet.
    Ich füge |ios::app hinzu, also:

    ofstream f("a", ios::binary|ios::app);
    f.write((char *)element_1, sizeof(*element_1));
    f.close();
    
    f("a", ios::binary|ios::app);
    f.write((char *)element_2, sizeof(*element_2));
    f.close();
    
    f("a", ios::binary|ios::app);
    f.write((char *)element_n, sizeof(*element_n));
    f.close();
    

    nun möchte ich die elemente wieder lesen.

    wie mache ich das?



  • Um eine Klasse serialisierbar zu machen, das heißt, um sie in einen Stream schreiben und wieder auslesen zu können, gibt es verschiedene Möglichkeiten, aber auf keinen Fall was du da machst fortsetzen!!

    Du kannst die Streaming-Operatoren überladen. Das ist am Anfänger-freundlichsten und einfachsten, es gibt dazu auch viel Stoff im Netz, ➡ Google: c++ overload operator<<
    So sollte es am Ende aussehen:

    class element_t
    {
        :::
    
    public:
    
        friend std::ostream& operator<<(std::ostream& os, element_t const& e)
        {
             // Die ganzen Member von e in os reinschreiben
             return os; // Referenz auf Stream zurückgeben -> damit os << a << b << c möglich wird
        }
    
        friend std::istream& operator>>(std::istream& is, element_t& e)
        {
             // Den ganzen Kram von oben extrahieren, aus is 
             return is; // ~ zurückgeben -> damit is >> a >> b >> c möglich wird.
        }
    };
    

    Das ganze ist dazu gut, dass du dann element_t einfach wie einen Skalar schreiben und lesen kannst:

    element_t t;
    
    // Mach was mit t ...
    
    std::ofstream os("Bla.txt");
    os << t; //Einfach reinschreiben
    

    dito fürs extrahieren:

    std::ifstream stream("Bla.txt");
    element_t e;
    stream >> e;
    

    Später kannst du dir auch noch Boost.Serialization ansehen... aber lerne estmal überladen der Streaming-Ops.



  • Sone schrieb:

    Um eine Klasse serialisierbar zu machen, das heißt, um sie in einen Stream schreiben und wieder auslesen zu können, gibt es verschiedene Möglichkeiten, aber auf keinen Fall was du da machst fortsetzen!!

    Wieso sagst du nicht weshalb?



  • Wie du richtig erkannt hast, bin ich mit dem Überladen der <<operatoren nicht
    vertraut.

    Auf hinweis aus diesem Forum habe ich Serialisierung gegoogelt und bin dabei auf folgendes Beispiel gekommen:
    http://www.functionx.com/cpp/articles/serialization.htm

    Daher war ich überzeugt, dass der Weg funktioniert. In dem Beispiel habe ich vermisst, dass die Daten mehrerer Schüler abgespeichert bzw. gelesen werden.
    Die Schulklasse besteht ja wohl kaum aus dieser einzelnen Schülerin.



  • Ja, erhole dich gut.
    Aber erkär mir bitte wo das Problem liegt

    Was du da tust ist gefährlich und unsicher.
    Sobald Referenzen oder Zeiger indirekt oder direkt (mit einem string oder dynamischen STL-Containern bspw. schon indirekt) deine Member sind, hast du ein gewaltiges Problem, was sich beim Einlesen in seiner vollen Pracht entfaltet:
    Du schreibst nur die Adresse des Pointees rein (oder im Falle der Referenz: die Adresse des Referenten), also einfach eine Zahl, aber nicht das, was referenziert, bzw. auf was gezeigt wird! Wenn du dann einliest, zeigen diese Zeiger auf völlig wildes Gebiet.
    Sobald du anfängst damit irgendwas zu machen wirst du Programmabstürze usw. erzeugen - es ist dasselbe wie mit einem un-initialisierten Zeiger zu arbeiten.
    :xmas1:

    reinterpreter schrieb:

    Sone schrieb:

    Um eine Klasse serialisierbar zu machen, das heißt, um sie in einen Stream schreiben und wieder auslesen zu können, gibt es verschiedene Möglichkeiten, aber auf keinen Fall was du da machst fortsetzen!!

    Wieso sagst du nicht weshalb?

    Stellt dich das zufrieden?



  • Auf hinweis aus diesem Forum habe ich Serialisierung gegoogelt und bin dabei auf folgendes Beispiel gekommen:
    http://www.functionx.com/cpp/articles/serialization.htm

    Vergiss das bitte, das Tutorial ist Blödsinn.



  • Sone schrieb:

    Stellt dich das zufrieden?

    Nein, du hast das grösste Problem nicht genannt. Sobald er den Compiler updated oder das Ziel-OS wechselt funktioniert es nicht mehr. Selbst wenn er nur "geeignete" Klassen nimmt, die keine Pointer und Referenzen enthalten.



  • Sone schrieb:

    Vergiss das bitte, das Tutorial ist Blödsinn.

    👍



  • reinterpreter schrieb:

    Sone schrieb:

    Stellt dich das zufrieden?

    Nein, du hast das grösste Problem nicht genannt. Sobald er den Compiler updated oder das Ziel-OS wechselt funktioniert es nicht mehr. Selbst wenn er nur "geeignete" Klassen nimmt, die keine Pointer und Referenzen enthalten.

    Wieso? Ich dachte (ah, ich denke zu viel 😃 ) dass der Offset gleich bleibt... tut er nicht? Naja, ist ja auch nicht standardisiert, oder?



  • Einerseits können chars und ints unterschiedlich gross sein, andererseits kann das Padding variieren. Zumindest kommen mir diese beiden in den Sinn, gibt vielleicht noch mehr Gründe. Was meinst du mit Offset?



  • vielen Dank für eure Hilfe,
    ich bin gerade ein wenig überfordert.
    Muss mich da einlesen, aber so wie sone das in der ersten Antwort
    geschrieben hat, ist genau das, was ich tun will.
    Ich muss mich daa gerade mal hineinlesen.
    Werde bestimmt noch eure Hilfe brauche, bis das funktioniert.

    Wieso ist folgendes Tutorial blödsinn? es behandelt genau die Problemstellung, mit welcher in konfrontiert bin. Nur verstehe ich nicht, wieso das tutorial nicht berücksichtigt, dass eine Schulkasse im allgemeinen mehr als eine schülerin hat.
    http://www.functionx.com/cpp/articles/serialization.htm


  • Mod

    sulky schrieb:

    Wieso ist folgendes Tutorial blödsinn?

    Es ist eben schlichtweg falsch. Nicht nur, dass der Code eher von mäßiger Qualität ist, ist auch das Grundkonzept, welches vorgestellt wird, falsch. Das heißt du lernst durch dieses Tutorial, etwas grundsätzlich Falsches auf eher mäßige Art und Weise umzusetzen.



  • bin gerade überfordert.

    ist das so richtig?

    class element
    {
    public:
    int a;
    string s;
     //element*next; //den zeiger lasse ich für den Anfang noch weg.
    
    friend ostream& operator<<(std::ostream& os, element const& e)
        {    // Die ganzen Member von e in os reinschreiben
             os<<e.a<<e.s;//<<e.next;  //schreibt man so die member in os?
    
             return os; // Referenz auf Stream zurückgeben -> damit os << a << b << c möglich wird
        }
    
    friend istream& operator>>(std::istream& is, element& e)
        {
             is>>e.a>>e.s;//>>e.next;
             //extrahiere ich das so?
             return is; // ~ zurückgeben -> damit is >> a >> b >> c möglich wird.
        }
    };
    

  • Mod

    Das ist ein guter Anfang, ja. Vermutlich willst du aber noch irgendeine Art von Formatierung schreiben, damit du bei komplizierteren Datentypen (wie z.B. std::string oder eben auch deiner eigenen Klasse) sicher gehen kannst, dass das Einlesen auch wirklich die Umkehrung des Schreibens ist. Zum Beispiel ist

    std::string foo = "Hallo Welt";
    datei_raus << foo;
    
    // ...
    
    datei_rein >> foo;  // Nur "Hallo"
    


  • Ja genau, mit diesem Problem bin ich bereits konfrontiert.
    Wie macht man das?

    aber das noch viel grössere Problem ist folgendes:
    Wenn ich die Klasse element erweitere um:

    element*next;
    

    so kann ich in der einschreibefunktion noch schreiben:

    os<<e.a<<e.s<<e.next;
    

    aber in der ausgabefunktion folgt eine Fehlermeldung auf

    is>>e.a>>e.s;>>e.next;
    

    wieso das?



  • reinterpreter schrieb:

    Einerseits können chars und ints unterschiedlich gross sein, andererseits kann das Padding variieren. Zumindest kommen mir diese beiden in den Sinn, gibt vielleicht noch mehr Gründe. Was meinst du mit Offset?

    Na den Member-Offset. Die Anzahl Bytes, die ein Member von seiner Instanz im Speicher entfernt liegt.

    xff65464e Instanz
    xff65464e Erster Member
    0ff654653 Zweiter Member
    :::

    Ja, die Skalare können unterschiedliche Größen haben; das stimmt.



  • Ja genau, mit diesem Problem bin ich bereits konfrontiert.
    Wie macht man das?

    Größe mit serialisieren.
    Also zuerst Größe reinschreiben und dann den String. Anschließend zuerst die Größe auslesen und dementsprechend lesen.



  • @Sone:
    Sorry aber deine Lösung ist für mich ein Hack. Wer garantiert dass das Format sauber dokumentiert wird, s.d. in 5 Jahren, wenn jemand den Code wieder anfasst, das Datenformat auch ohne Probleme versteht.

    Was spricht dagegen in solchen Fällen die Klassendefinitionen ins XML Format zu wandeln. Ist lesbarer als deine Lösung und eindeutiger definiert.

    class ABC
    {
    public:
      std::string Name;
      std::string NachName
    };
    

    wird zu

    <abc name="Hallo" NachName="Welt"/>
    


  • Vermutlich spricht gegen XMl, dass XML kacke ist. Sones Loesung ist, auch wenn ich es ungern zugebe, ausnahmsweise mal korrekt und imo die beste Loesung.



  • Hallo sulky ,
    wieso hast du bis jetzt noch nich die Definition der Klasse gezeigt? Dann wüssten wir, um wie viele Attribute und um welche Datentypen es sich handelt. Dann ließe sich dir auch besser helfen.


Anmelden zum Antworten