Struct mit AnsiString speichern



  • lattemän schrieb:

    Old McDonald schrieb:

    Das hier:

    fwrite(BenDat,sizeof(struct  Pay), sizeof(BenDat),f);
    

    Erzeugt einen Pufferüberlauf.

    Auch du Scheiße. Da haddu recht, was bin ich doch für eine Schlampe :D, da hab ich gepennt.

    Den Teiler habe ich ganz vergessen!
    Korrekt wäre in der Tat gewesen:

    fwrite ( BenDat, sizeof( struct Pay ), sizeof( BenDat )/sizeof(struct Pay), fp);
    

    für die sogenannten PODs. -> hier kein rolleyes <- 🙂

    Benutze in sizeof bitte immer Ausdrücke und keine Typbezeichner, wenn BenDat jemals kein Array von Pays mehr ist, dann ist dein Code falsch, während der von Old McDonald weiterhin das richtige tut.



  • Tippgeber schrieb:

    Benutze in sizeof bitte immer Ausdrücke und keine Typbezeichner, wenn BenDat jemals kein Array von Pays mehr ist, dann ist dein Code falsch, während der von Old McDonald weiterhin das richtige tut.

    Ja, das mach Sinn.



  • Schon mal vielen dank für die Mühen, dachte das Problem sei schneller gelöst. 🙂

    Ich habe es nun auch mit dem Teiler versucht, aber das Programm stürzt desswegen ab. Aber es sei ja so oder so falsch, wollte es nur mal ausprobieren.

    Viel Spaß. Denn wie du die Zeichenketten variabler Länge wieder korrekt auslesen willst, hast du noch nicht gesagt; zumindest nicht eindeutig genug, dass es Mathiable versteht.

    Mein wissen beschränkt sich anscheinend nur auf "Steinzeit C" und das auch nur Grundlegend, also ist es berechtigt anzunehmen das ich es nicht verstehe 😉

    Ich habe im Moment eine zwischenlösung, bei der der AnsiString zuerst in ein Char umgewandelt und dann abgespeichert wird. Das Funktioniert so weit, ist aber nicht gerade elegant gelöst...



  • Nein, verstehen wirst du's schon, aber an der Beschreibung von lattemann wirst du vermutlich scheitern 😉

    Das Problem ist, dass du auf Pay nicht wie auf rohem Speicher arbeiten kannst. AnsiString ist eine Klasse, die Speicher alloziert und freigibt und einen Zeiger auf den Speicher enthält. Kopierst du nur den rohen Speicher, hast du dann den Zeiger in der Datei, eigentlich willst du dann aber die Zeichenkette drin haben. Beim Einlesen wird dann einfach wieder irgendein alter Zeiger eingelesen. Wird das AnsiString-Objekt nun zerstört, will es den Speicher freigeben und es macht bumm.
    Also musst du alles einzeln speichern und nicht einfach den Speicherblock. Aber nur die Zeichenkette selbst zu schreiben, reicht natürlich nicht aus - du musst auch irgendwoher wissen, wie lang diese ist.

    Hm, also am besten schreibst du dir einige Funktionen, die das Lesen und Schreiben vornehmen. Der Schreibvorgang könnte z. B. mit C++-Streams so aussehen (Achtung! nicht durch einen Kompiler gejagt!):

    #include <ostream>
    // int schreiben
    void write(std::ostream &stream, int value)
    {
        stream.write(reinterpret_cast<const char*>(&value), sizeof(value));
    }
    
    // Zeichenkette (AnsiString) schreiben
    void write(std::ostream &stream, const AnsiString &value)
    {
        int length = value.Length(); // int AnsiString::Length() laut kurzer Googlesuche
        write(stream, length); // wir müssen die Länge speichern, um die Zeichenkette wieder korrekt auszulesen
        stream.write(value.c_str(), length); // und dann die Zeichenkette selbst natürlich auch noch
    }
    
    // Bezahlungsdaten schreiben
    void write(std::ostream &stream, const Pay &payment)
    {
        write(stream, payment.Name);
        write(stream, payment.PW);
        write(stream, payment.Strasse);
        write(stream, payment.Nr);
        // ...
    }
    
    // Funktion zum Schreiben von Feldern (dann brauchen wir keine Schleife in u_save() zu schreiben)
    template <typename T, std::size_t N>
    void write(std::ostream &stream, T (&array)[N])
    {
        for (std::size_t i = 0; i < N; ++i)
        {
            write(stream, array[i]);
        }
    }
    

    und voilà:

    #include <fstream>
    void u_save() 
    { 
        std::ofstream file("C:/Data.bin", std::ios_base::binary | std::ios_base::out);
    
        write(file, BenDat);
    }
    

    Der Lesevorgang sollte nun leicht zu schreiben sein.
    Wenn du bei den C-Streams bleiben willst, sollte es auch nicht allzu schwer sein, den Quelltext oben umzuschreiben (statt den std::ostream & -Parametern dann halt FILE* und statt stream.write(...) fwrite(..., stream) )

    Vielleicht überlegst du dir auch noch einmal, ob du nicht std::string nehmen willst - das hier ist das Standard-C++-Forum 😉



  • Vielleicht überlegst du dir auch noch einmal, ob du nicht std::string nehmen willst - das hier ist das Standard-C++-Forum 😉

    Ja hab ich mir schon gedacht. Hatte mal das Borland spezifische Forum gefunden, aber irgendwie wieder aus den Augen verloren. :p

    Dass mit dem schreiben funktioniert schon ganz gut, er schreibt eine 2 KB grosse datei mit den Daten. Das Lesen will allerdings noch nicht so recht. Er meckert immer, dass er einen Char erwartet und einen constant char bekommt.

    stream.read(reinterpret_cast<const char*>(&value), sizeof(value));
    


  • Was ist value für ein Typ und wieso willst du den casten? 😕



  • Hm, ja, in Konstanten kann man schlecht schreiben, oder? 🙂

    Ich schreibe dir mal eine read()-Funktion, die anderen wirst du wohl selbst hinbekommen:

    // int lesen
    void read(std::istream &stream, int &value)
    {
        stream.read(reinterpret_cast<char*>(&value), sizeof(value));
    }
    


  • Old McDonald schrieb:

    Hm, ja, in Konstanten kann man schlecht schreiben, oder? 🙂

    Macht sinn ja, hätte ich selbst drauf kommen müssen. :p

    Kompilieren kann er es jetzt, aber er liest nichts raus...

    ich hab auch irgendwie Zweifel an der einen Zeile:

    stream.write(value.c_str(), length); // und dann die Zeichenkette selbst natürlich auch noch
    

    kann es sein dass er length und value ständig wider überschreibt? und wenn nicht, woher soll er wissen welches length für welches value ist?



  • stream.write() ist schon einmal falsch - es müsste stream.read() heißen.

    AnsiString::c_str() ist eine merkwürdige Funktion. Sie gibt einen char* zurück, nicht einen const char*, wie man erwartet.
    Ohne mich mit C++-Builder und AnsiString auszukennen, rate ich mal ins Blaue hinein, wie es funktionieren könnte:

    void read(std::istream &stream, AnsiString &value)
    {
        int length;
        read(stream, length); // die Größe lesen
        value.SetLength(length); // der Puffer muss groß genug sein
        stream.read(value.c_str(), length); // und dann die Zeichenkette auslesen
    }
    


  • Wow, es hat sogar funktioniert. Danke tausendmal für die Geduld und Mühen. Ich spendier dir bei Gelegenheit mal ne runde. 😉


Anmelden zum Antworten