Daten aus Klassen in Dateien schreiben



  • Hallo Community,

    ich habe schon viel in Foren gesucht und mich in verschiedenen Büchern zu diesem Thema eingelesen. Aber ich komme nicht weiter und bräuchte vielleicht mal einen Tip.

    Folgendes Problem:
    Ich habe mehrere Klassen in denen ich in den Private Bereich Daten über Funktionen hineingebe. Diese Daten beziehen sich auf verschiedene Bauteile die ich mit Werten abspeichern möchte.

    In einer Gesamtklasse rufe ich die einzelnen Klassen auf um sowas wie eine gesamte Struktur zu bilden, da ich diese Daten als eine Einheit speichern und wieder laden möchte. Ich muß aber die Möglichkeit haben auf einzelne Teile auch separat zugreifen zu können.
    Ich weiß, dass ich auf diese eingebundenen Klassen nicht einfach so auf die Privaten Daten zugreifen kann. Nur wie bekomme ich das am geschicktesten hin.

    Über friend Funktionen oder soll ich die gesamte Klasse als friend deklarieren? Und wenn ich die Daten nur in der Gesamtklasse reinladen möchte, wie kann ich die dann in eine Datei Streamen?

    class KompoMass
            {
             private:                            // Private Bereich
             char Komponente[25];                // Komponentenname
             char Masseinheit[3];                // Masseinheit
             public:                             // Public Bereich
             KompoMass();                        // Konstruktor
             ~KompoMass();                       // Destruktor
    
             // Daten in Private Bereich übergeben
             void speicherDaten(const char *Komp, const char *Mass);
    
             // Komponente aus Datenbereich holen
             AnsiString holeKomponente(void);
    
             // Masseinheit aus Datenbereich holen
             AnsiString holeMasseinheit(void);
    
            };
    
    // Klasse anlegen und ableiten von Klasse KompoMass
    class EineFreq : public KompoMass
            {
             private:                            // Private Bereich
             float Minimum;                      // Frequenz
             int Anzahl;                         // Anzahl Messungen
             friend class GesamtFreq;
             public:                             // Public Bereich
             EineFreq();                         // Konstruktor
             ~EineFreq();                        // Destruktor
    
             // Daten in Private Bereich übergeben
             void speicherWerte1(AnsiString MinFreq, AnsiString Anz);
    
             // Minimum Frequenz aus Datenbereich holen
             float holeMinimum(void);
    
             // Anzahl aus Datenbereich holen
             int holeAnzahl(void);
            };
    
    class GesamtFreq
            {
             public:
             class EineFreq Bauteil1;
             class EineFreq Bauteil2;
            };
    

    Muß ich mir nun in der Klasse GesamtFreq eine Datenstruktur mit struct anlegen mit allen Daten, damit ich die Werte dann von den einzelnen Klassen mit den "hole Anweisungen" in diese Struktur schieben kann oder gibt es eine Möglichkeit, wie ich über eine Funktion in der GesamtFreq auf alle privaten Bereiche zugreifen kann um die nacheinander über folgenden Code in eine Datei zu schieben.

    ofstream ausgabe("Grenzwerte.txt",ios_base::out | ios_base::binary);
             ausgabe.write(reinterpret_cast<char *>(&GesamtFreq)
             ,sizeof(GesamtFreq));           
             ausgabe.close();
    

    Eigentlich habe ich ja in der GesamtFreq ja schon eine Klasse wo alle Daten verfügbar wären.

    Und mit GesamtFreq.Bauteil kann ich darauf zugreifen, nur sind da natürlich keine Werte drin.

    Vielleicht kann mir jemand einen guten Tip geben, wie man mit diesem Problem umzugehen hat. Denn ich hänge seit ein paar Tagen daran.

    Bin für jede Hilfe dankbar.



  • Wo ist denn jetzt dein Problem? In "GesamtFreq.Bauteil" sind natürlich die Werte der Klasse drin - nur kommst du dort nicht direkt ran, weil sie privat sind. Um die Werte zu erhalten, mußt du die öffentliche Schnittstelle nutzen - hier also die Methoden "holeMinimum()" und "holeAnzahl()".

    Übrigens sollten die einzelnen Klassen selber in der Lage sein, ihre eigenen Daten in einen Stream zu schreiben bzw. wieder auszulesen.

    PS: Dieses Mischmasch aus char-Arrays und AnsiString's sieht ja nicht mehr feierlich aus (noch schlimmer finde ich, daß du AnsiString's für Daten übergibst, die eigentlich Zahlen darstellen sollen).



  • Hallo CStoll,

    ich übergebe AnsiString, weil ich die Daten gleich im Borland Builder in ein Label oder in ein Edit Feld schreibe und mir die Umwandlung auf der Visuseite damit sparen möchte.

    Zu meinem Problem ich habe ein Eingabeformular mit Grenzwerten, das ich auf der Visuseite mit GesamtFreq User starte. Die Daten die dort reingeschrieben werden, sollen in eine Datei Grenzwerte. Wenn aber der User keine eigenen Grenzwerte festlegen möchte, dann wird GesamtFreq Grundeinstellung geladen.

    Ich weiß jetzt nicht, wie ich es fertig bringe, dass die Daten die in der GesamtFreq User drin stehen oder besser gesagt in Bauteil1 - KompoMass, Bauteil1 - EineFreq, z.B. mit der Funktion void speichereDaten(GesamtFreq) so aus den Unterklassen lesen kann, das ich die Werte auch in eine Datei schieben kann.

    Da ich ja in GesamtFreq nur auf die Schnittstellenfunktionen der Unterklassen zugreifen kann. Wie speichere ich diese Daten dann zusammenhängend, ähnlich wie in einer Struktur, bei der ich sagen kann void speichereDaten(strukturname);

    Wie muß da mein Zugriff auf auf die Unterklassen erfolgen, über Rückgabewerte, die ich dann wieder in einer Struktur die ich in GesamtFreq erstellen muß speichere. Oder gibt es eine Möglichkeit mit der ich gleich die Daten aus den Unterklassen nacheinander einlese und in eine Datei schiebe. Eventuell über eine Friend Funktion?



  • Da mußt du schon der Struktur eine Funktion "SpeichereDaten()" (und "LeseDaten()") spendieren, die das übernimmt (und dann auch selber entscheidet, wie).



  • Das heißt, ich muß in GesamtFreq die zwei Funktionen "SpeichereDaten()" und "LeseDaten()"anlegen und in diesen Funktionen jeweils die holeKomponente, bzw. schreibeDaten aus den Unterklassen ansprechen um diese dann in eine Struktur zu schreiben, die ich wiederum speichern kann. Habe ich das so richtig verstanden?

    Geht es nicht, dass ich irgendwie in der GesamtFreq, die ja schon alle Daten hat, sowas wie zeigeDaten aufrufe, diese dann alle Unterklassendaten aufruft und ich diese als direkten Stream abspeicher.

    Ich habe auch versucht, den Code zum Speichern in die UnterKlassen einzubauen, da haut das hin, aber es würde ja, bei jedem Aufruf der Unterklassen die Datei geöffnet und geschlossen werden, was mir nicht als sehr Sinnvoll erscheint, wenn ich die Unterklassen 10 mal aufrufe um mir meine Daten abspeichern zu können.

    Aber ich geb dir Recht, wenn ich die Daten im reinem Rohformat speichere und zurückgebe ist das besser. Die Visuseite sollte selber entscheiden wie sie die Daten umkonvertiert, dass muß nicht auf der Datenseite passieren, werde das umstricken. Danke für den Tip.



  • egcactus schrieb:

    Geht es nicht, dass ich irgendwie in der GesamtFreq, die ja schon alle Daten hat, sowas wie zeigeDaten aufrufe, diese dann alle Unterklassendaten aufruft und ich diese als direkten Stream abspeicher.

    Es ginge sicher, wenn da nicht ein entscheidendes Detail wäre - "private". GesamtFreq darf nicht direkt auf die Daten seiner Member zugreifen, sondern nur über deren öffentliche Zugriffsmethoden.

    Ich habe auch versucht, den Code zum Speichern in die UnterKlassen einzubauen, da haut das hin, aber es würde ja, bei jedem Aufruf der Unterklassen die Datei geöffnet und geschlossen werden, was mir nicht als sehr Sinnvoll erscheint, wenn ich die Unterklassen 10 mal aufrufe um mir meine Daten abspeichern zu können.

    Du könntest auch die Datei einmal öffnen und dann als Parameter an die Speichern-Funktion(en) übergeben 😉



  • Als Parameter übergeben, meinst du damit, ich öffne die Datei in GesamtFreq

    ofstream ausgabe("Grenzwerte.txt",ios_base::out | ios_base::binary);
    

    schreibe in den Dateien eine Funktion mit holeDaten und schreibe

    ausgabe.write(reinterpret_cast<char *>(&jeweiligerDatenTyp)
             ,sizeof(jeweiligerDatenTyp));
    

    und schließe dann wieder in GesamtFreq

    ausgabe.close();
    

    Oder meinst du das anders?



  • Ich meinte damit, daß du den Schreib-Methoden einen ostream übergibst - das tatsächliche Schreiben bleibt den Unterklassen überlassen:

    class EineFreq : public KompoMass
    {
      float Minimum;
      int Anzahl;
    public:
      ...
      void speichere(ostream& out)
      {
        KompoMass::speichere(out);
        out<<Minimum<<" "<<Anzahl<<endl;
      }
    };
    
    class GesamtFreq
    {
      EineFreq Bauteil1;
      EineFreq Bauteil2;
    public:
      ...
      void speichere(ostream& out)
      {
        Bauteil1.speichere(out);
        Bauteil2.speichere(out);
      }
    };
    
    ...
    GesamtFreq Anlage;
    ...
    ofstream ausgabe(...);
    Anlage.speichere(ausgabe);
    


  • Ok, ich verstehe es nun. War eine schwere Geburt.

    Ich baue noch etwas meine Aufrufe um, damit die Umkonvertierung der Daten auf der Visuseite passiert und nicht mehr in den Klassen und dann werde ich das mit der Übergabe des ostream probieren.

    Vielen Dank nochmal, du hast mir sehr geholfen. 👍

    Grüße aus dem Schwabenländle.



  • Also habe das nun mal eingebaut, aber irgendwie funktioniert das nicht.

    Sobald ich die Zeile

    void speichere(ostream& out);
    

    drin habe, bekomme ich gleich die Meldung:

    ) erwartet und bleibt nach dem & stehen. Der Compiler nimmt die Zeile nur, wenn ich & out weglasse, dann funktioniert aber der Rest nicht, da er ja das out nicht mehr kennt.

    Was mache ich falsch?



  • Wo hast du denn diese Zeile stehen? Und kennt der Compiler auch die Klasse "ostream" (#include <iostream> - und den Namensraum std:: beachten)?



  • Ich habe in der Headerdatei KompoMass eine Funktion angelegt.

    void speichere(ostream& out);
    

    danach habe ich noch versucht #include <ostream> ging aber nicht und mit #include <iostream> geht es auch nicht.

    Habe den restlichen Code auskommentiert, so dass eigentlich nur noch diese Anweisung drin bleibt, dadurch dürfte er auch nicht mit dem namespace in einem konflikt geraten.



  • Hast du mal versucht, den Namensraum explizit anzugeben?

    #include <iostream>
    
    void speichere(std::ostream& out);
    


  • Hey, das wars.
    Aber warum ich den Namensraum explizit angeben muß ist mir nicht klar.

    Werde mal den restlichen Code aktivieren und mal schauen was passiert.
    Nochmals danke.



  • egcactus schrieb:

    Hey, das wars.
    Aber warum ich den Namensraum explizit angeben muß ist mir nicht klar.

    Weil so ziemlich alle Bestandteile der Standardbibliothek in diesem Namensraum untergebracht sind 😉 Damit hast du drei Möglichkeiten, an den Bezeichner heranzukommen - explizite Namensraum-Angabe "std::ostream" (gilt nur für diese eine Code-Stelle), using-Deklaration "using std::ostream;" (gilt ab da für alle Vorkommen von ostream) oder using-Direktive "using namespace std;" (gilt für ALLE Bezeichner aus dem Namensraum) - letzteres ist für Header nicht zu empfehlen.



  • Also es kam noch ein kleiner Fehler: Der Compiler bemängelte im Formular von wo ich das aufrufe, dass er "ofstream" nicht kennt, aber da hat dann nur die

    #include <fstream>
    

    Anweisung noch gefehlt.

    Aber der Rest funktioniert einwandfrei.

    Werde das ganze noch etwas umgestalten, so das der ofstream Aufruf in der Klasse GesamtFreq erfolgt und dass man den Dateinamen ändern kann, aber sonst ist das absolut Klasse.

    Vielen herzlichen Dank nochmal. Denke wir werden uns bestimmt wieder sehen. Bin an diesem Projekt noch eine Weile beschäftigt. Und wenn wieder eine Frage auftaucht, die mich nicht schlafen lässt, dann weiß ich nun, dass ich hier Hilfe bekomme.

    Viele Grüße aus dem Schwabenländle.



  • Ich habe eine neue Lösung für mein Problem, habe es nun so gelöst:

    Die Klasse GesamtFreq ruft die Unterklassen auf, in denen die als PRIVATE Werte gespeichert werden.

    class GesamtFreq
            {
             public:
             class EineFreq Bauteil1;
             class EineFreq Bauteil2;
    
             // Daten in Datei schreiben
             void speicherInDatei(GesamtFreq);
    
             // Daten aus Datei lesen
             GesamtFreq leseAusDatei(void);
    

    In den Funktionen speicherInDatei und leseAusDatei werden die Daten gespeichert bzw. geladen.

    void GesamtFreq::speicherInDatei(GesamtFreq UserFreq)
            {
             ofstream fout("Datei.txt",ios::binary);
             fout.write((char*) &UserFreq, sizeof UserFreq);
             fout.close();
            }
    
    GesamtFreq GesamtFreq::leseAusDatei(void)
            {
             GesamtFreq DefaultFreq;
             ifstream fin("Datei.txt",ios::binary);
             fin.read((char*) &DefaultFreq, sizeof DefaultFreq);
             fin.close();
             return DefaultFreq;
            }
    

    Was hälst du von dieser Lösung? Oder verstosse ich gegen jede Regel der Programmierung?

    Ich spare mir dadurch den ostream zu übergeben und danach die Daten mühselig wieder zu trennen. Da ich durch die verschiedenen Stringglängen bei den Komponenten und dadurch, dass ich mir die Masseinheiten, je nachdem in welchem Spektrum es gemessen werden soll bei jedem Bauteil, mehrmals notiere, kam ich immer wieder zu Verschiebungen in den Daten beim wieder einlesen.

    Grüße aus dem Schwabenländle.


Anmelden zum Antworten