Struct in Datei schreiben aus CodeGuru.com



  • Ich bin zufällig auf der Seite CodeGuru.com über folgenden Code gestolpert :

    #include <string>
    #include <fstream>
    
    struct s
    {
      // Your POD data here
    };
    
    void write(const std::string& file_name, s& data)
    {
      std::ofstream out(file_name.c_str());
      out.write(reinterpret_cast<char*>(&s), sizeof(s));
    }
    
    void read(const std::string& file_name, s& data)
    {
      std::ifstream in(file_name.c_str());
      in.read(reinterpret_cast<char*>(&s), sizeof(s));
    }
    
    int main()
    {
      s myStruct;
      read("test.dat", myStruct);
      write("test.dat", myStruct);
    }
    

    Ich bin eigentlich noch nicht soweit , bzw. hab auch noch keinen Anwendungsfall, ich würde nur gerne wissen ob das so "richtig gemacht" ist? Mir kommen die Cast´s etwas viel vor?

    Und da ich das Thema sowieso irgendwann mal brauche.... Wie wird´s richtig gemacht?



  • Das ist so schon okay. An einer Stelle könnte noch ein const hinzugefügt werden, aber sonst ist okay.

    Vorrausgesetzt, in dem struct stehen wirklich nur die Basisdatentypen.



  • Die Casts sind zwar okay, aber es ist trotzdem eine doofe Idee, weil es Padding geben kann.



  • Die Casts sind zwar okay, aber es ist trotzdem eine doofe Idee, weil es Padding geben kann.

    Ok, wie würdest du das dann lösen? Möchte mir gleich zu anfangs einen "guten Stil" aneignen.

    Die Casts sind zwar okay

    Irgendwer hat mir mal beigebracht, "Wenn du casten musst, hast du was falsch gemacht". Darum hab ich nachgefragt



  • cpp_beginner_offl schrieb:

    Die Casts sind zwar okay, aber es ist trotzdem eine doofe Idee, weil es Padding geben kann.

    Ok, wie würdest du das dann lösen? Möchte mir gleich zu anfangs einen "guten Stil" aneignen.

    Kodiere Informationen für den Austausch am besten als JSON. Da kannst du als Anfänger weniger falsch machen. Für C++ gibt es eine gute Bibliothek für JSON.

    cpp_beginner_offl schrieb:

    Die Casts sind zwar okay

    Irgendwer hat mir mal beigebracht, "Wenn du casten musst, hast du was falsch gemacht". Darum hab ich nachgefragt

    Ein reinterpret_cast wird nur für nicht portable Sachen gebraucht. So etwas sollte man vermeiden.



  • cpp_beginner_offl schrieb:

    Ok, wie würdest du das dann lösen? Möchte mir gleich zu anfangs einen "guten Stil" aneignen.

    Die Standardloesung waere wohl, eine Funktion dafuer zu schreiben und die Member einzeln zu serialisieren. Z.B.:

    struct X
    {
        int i;
        std::string s;
    };
    
    void serialize(std::ostream& os, X const& x)
    {
        serialize(os, x.i);
        serialize(os, x.s);
    }
    

    Man baut das ganze also rekursiv auf. Am Ende gibts Serialisierungsfunktionen fuer die ganzen primitiven Typen...

    void serialize(std::ostream& os, int i)
    {
        serialize(os, &i, sizeof i);
    }
    

    ... die dann wiederum alle eine einzige Funktion verwenden koennen, die den Cast ausfuehrt:

    void serialize(std::ostream& os, void const* data, std::size_t size)
    {
        os.write(static_cast<char const*>(data), size);
    }
    

    Damit hat man das Schreiben nur an genau einer einzigen Stelle.

    cpp_beginner_offl schrieb:

    Irgendwer hat mir mal beigebracht, "Wenn du casten musst, hast du was falsch gemacht". Darum hab ich nachgefragt

    Vermeide Casts, wenn moeglich. Aber es gibt Faelle (z.B. hier) wo man sie braucht.



  • cpp_beginner_offl schrieb:

    Die Casts sind zwar okay, aber es ist trotzdem eine doofe Idee, weil es Padding geben kann.

    Ok, wie würdest du das dann lösen? Möchte mir gleich zu anfangs einen "guten Stil" aneignen.

    Überleg dir ein Format und schreib deine Datenelemente dementsprechend in deine Datei. Für's Einlesen baust du dir dann einfach eine Einlesefunktion, entsprechend dem Dateiformat.
    (Wenn ich mich recht erinnere, hat Werner das mal so empfohlen)

    cpp_beginner_offl schrieb:

    Die Casts sind zwar okay

    Irgendwer hat mir mal beigebracht, "Wenn du casten musst, hast du was falsch gemacht". Darum hab ich nachgefragt

    Ne. Es gibt auch gute und sinnvolle Casts.



  • Überleg dir ein Format und schreib deine Datenelemente dementsprechend in deine Datei.

    Glaub das verstehe ich jetzt nicht richtig. Das Format wäre doch schon meine Struct?

    @Kellerautomat : Danke für´s Beispiel, werd das mal testen. Das ganze sind quasi überladene Funktionen weil sie alle gleich heißen und sich durch ihre Parameter unterscheiden, oder ? Bin ehrlichgesagt noch nicht soo weit mit meinem C++ Buch, bin wie gesagt zufällig drüber gefallen und da ich in C sowas schonmal gemacht hatte , hat mich das "Pendant" in C++ interessiert.



  • cpp_beginner_offl schrieb:

    Überleg dir ein Format und schreib deine Datenelemente dementsprechend in deine Datei.

    Glaub das verstehe ich jetzt nicht richtig. Das Format wäre doch schon meine Struct?

    Mit Format meine ich Dateiformat.

    struct anschrift
    {
    string vorname;
    string nachname;
    string strasse;
    string hausnummer;
    string ort;
    };
    

    Dateiformat: Datenelement = Wert

    vorname = Hans
    nachname = Mayer
    strasse = Teststrasse
    hausnummer = 0815
    ort = Testort
    


  • ???



  • cpp_beginner_offl schrieb:

    Glaub das verstehe ich jetzt nicht richtig. Das Format wäre doch schon meine Struct?

    Kann man definitiv so machen. Gibt aber auch andere Moeglichkeiten, wie z.B. das schon erwaehnte JSON. Da du bei meiner Variante an einer einzigen Stelle das Schreiben hast, koenntest du da genauso auch JSON generieren und rausschreiben. Du koenntest es sogar Formatunabhaengig machen und das Format als (Template)Parameter uebergeben.

    cpp_beginner_offl schrieb:

    @Kellerautomat : Danke für´s Beispiel, werd das mal testen. Das ganze sind quasi überladene Funktionen weil sie alle gleich heißen und sich durch ihre Parameter unterscheiden, oder ? Bin ehrlichgesagt noch nicht soo weit mit meinem C++ Buch, bin wie gesagt zufällig drüber gefallen und da ich in C sowas schonmal gemacht hatte , hat mich das "Pendant" in C++ interessiert.

    Richtig, das ist Ueberladung. Ist nicht viel Magie dabei, wenn man sich auf einfache Faelle wie diesen beschraenkt.



  • Ok, danke erstmal. Werde das demnächst versuchen und dann evtl. das Thema hier nochmals aufgreifen.



  • Noch ein Zusatz:

    Was wenn meine Strukt nicht POD ist? z.B.: einen std::string enthält, oder enthalten soll? Ok, rausschreiben ist nicht das Problem, aber das anschließenden oder spätere Einlesen....

    Ich hab sowas jetzt über die POD Version gelöst und char irgendwas[XY] benutzt.

    Aber das "Problem" haben doch bestimmt schon mehrere gehabt, wie geht Ihr Profis sowas an?

    Ich dachte daran erst die "Länge" rauszuschreiben aber irgendwie lande ich immer wieder bein char array....



  • cpp_beginner_offl schrieb:

    Was wenn meine Strukt nicht POD ist? z.B.: einen std::string enthält, oder enthalten soll?

    Dann machst dus so wie Kellerautomat gesagt hat: Nicht dei struct einfach reinschreiben sondern jeden Member einzeln.
    Für string-member gibts 3 Möglichkeiten:

    • Nullbasiert, dh strings als C-string inklusive Nullbyte reinschreiben
    • Fixed size (d.h. immer gleich viele chars)
    • Die Länge des strings und dann den Inhalt

    Je nach Anforderungen. Ich würde eher zu Size+Inhalt tendieren. 🙂



  • •Die Länge des strings und dann den Inhalt
    Je nach Anforderungen. Ich würde eher zu Size+Inhalt tendieren.

    Ok, das leuchtet schon ein, aber wie lese ich das dann wieder ein?
    Mit der Länge und new[länge] ein char array anlegen?

    Und noch was :

    os.write(static_cast<char const*>(data), size);
    
       static_cast  oder reinterpret_cast  ... warum nicht ganz ohne Cast? 
    
    irgendwas.write( (char*)&myStruct sizeof(myStruct) );
    


  • beg_offl schrieb:

    Ok, das leuchtet schon ein, aber wie lese ich das dann wieder ein?
    Mit der Länge und new[länge] ein char array anlegen?

    char* pointerToData = "test"/* ... */;
    int dataLen = 4/* ... */;
    std::string data;
    data.append(pointerToData, dataLen);
    

    Oder wenn du es mit streams machst, kannst du die streams gleich alles machen lassen:

    fstream stream;
    stream >> data;
    

    beg_offl schrieb:

    Und noch was :

    os.write(static_cast<char const*>(data), size);
    
       static_cast  oder reinterpret_cast  ... warum nicht ganz ohne Cast? 
     
    irgendwas.write( (char*)&myStruct sizeof(myStruct) );
    

    Das ist doch auch ein Cast. Allerdings ein C-Style Cast. Da wir hier in C++ sind, nutzen wir auch C++-Style Casts.



  • DarkShadow44 schrieb:

    beg_offl schrieb:

    Ok, das leuchtet schon ein, aber wie lese ich das dann wieder ein?
    Mit der Länge und new[länge] ein char array anlegen?

    char* pointerToData = "test"/* ... */;
    int dataLen = 4/* ... */;
    std::string data;
    data.append(pointerToData, dataLen);
    

    Oder wenn du es mit streams machst, kannst du die streams gleich alles machen lassen:

    fstream stream;
    stream >> data;
    

    Ok, append muss ich erst in der eferenz nachsehen.

    stream >> data => das geht dann aber nur wenn in der Datei nur Text steht , bzw. ich alles alles in den String data einlesen will, hab ich das jetzt richtig verstanden?

    Generell sind wohl char Arrays in C++ nicht mehr angebracht ?



  • beg_offl schrieb:

    Generell sind wohl char Arrays in C++ nicht mehr angebracht ?

    Strings, die man bastelt, einliest und so, sollten keine char-Arrays mehr sein.
    Konstante Strings, die man eh nur dem Betriebssystem reicht, dürfen schon.

    string const filename="c:/autoexec.bat";//grotesk
    ...
    ifstream in(filename);
    


  • DarkShadow44 schrieb:

    beg_offl schrieb:

    Ok, das leuchtet schon ein, aber wie lese ich das dann wieder ein?
    Mit der Länge und new[länge] ein char array anlegen?

    char* pointerToData = "test"/* ... */;
    int dataLen = 4/* ... */;
    std::string data;
    data.append(pointerToData, dataLen);
    

    Oder wenn du es mit streams machst, kannst du die streams gleich alles machen lassen:

    fstream stream;
    stream >> data;
    

    Hmm, da hab ich jetzt doch noch ein anderes Problem. Wenn der String mit Zahlen beginnt....

    Ich schreibe raus mit : datei << length ; datei << myString .
    Beim Einlesen : datei >> len => wenn der String jetzt mit Zahlen beginnt, hab ich bei Länge Mist drin stehen


  • Mod

    Dann schieb Leerraum dazwischen. Bei Leerraum brechen formatierte Extraktionen nämlich ab. Also

    datei << length << ' ' << myString;
    

    zum schreiben und einlesen wie gewohnt.


Log in to reply