Structure-array speichern



  • Hallo Joe_M,
    danke für den Hinweis.
    na klar, ne Lagerverwaltung mit Datenbank.
    Aber ich will ein kleines Projekt ganz ohne Datenbank. Gruß von sehreilig

    Hallo Braunstein,

    die TStringList ist nicht so ein Problem, aber die TStreams, die du anwendest.

    Ich zeig mal hier meine nicht funktionierende Lösung.

    [cpp]
    struct Ding
            {
            AnsiString Name;
            int a;
            void write(AnsiString DatName);
            void read(AnsiString DatName);
            }     ;
    
    void Ding::write(AnsiString DatName)
    {
    TStringList *daten = new TStringList;
    daten->Add(Name);
    daten->Add(IntToStr(a));
    daten->SaveToFile(DatName);
    } ;
    
    void Ding::read(AnsiString DatName)
    {
    TStringList *daten = new TStringList;
    daten->LoadFromFile(DatName);
    Name = daten->Strings[0];
    a = StrToInt(daten->Strings[1]);
    } ;
    
    [/cpp]
    

    Ich finde keine Möglichkeit, um zum Speichern eines Arrays die Inhalte nacheinander in eine Datei zu schreiben. Gespeichert ist immer nur das letzte Array-Element.

    [cpp]
    //speichern
    
    Ding test[5]; 
    //hier folgt Schleife für Inhalte
    
    for (int i = 0; i<5; i++)
        {
        test[i].write("test.Dat") ;
        }
    
    //lesen
    
    Ding kurt[5];
    
    for (int i = 0; i<5; i++)
        {
        kurt[i].read("test.Dat") ;
        Label1->Caption = Label1->Caption + kurt[i].Name + "  " + kurt[i].a + " \n";
        }
    [/cpp]
    

    Weshalb das so ist, ist mir klar (datei wird beim Schreiben mit jedem Schleifendurchlauf neu angelegt...) 😕

    Es grüßt sehreilig



  • Als Denkanstoß: Beschäftige dich lieber mal mit (Datei-)streams. (BCB-Hilfe zu TFileStream).

    Dann kannst du ungefähr sowas machen:

    MeinStream = neuer Stream(Dateiname);
    iteriere über alle Elemente
        Element.write(MeinStream);
    Schließe Stream
    

    Lesen sähe genauso aus.

    /Edit: Man kann dasselbe Prinzip natürlich auch mit TStringList machen.



  • @Dasd
    Genau so hatte ich mir das gedacht.
    @sehreilig
    Irgendwie sehen meine read/write-Funktionen anders aus als deine und das hat auch seinen Grund 😉 .
    Bezüglich der Hilfe: die BCB-Hilfe funktioniert für TStream (oder hier besser TFileStream) genauso wie bei TStringList.



  • Dachte nur es wäre 'sehreilig'. Und da verstehe ich jetzt nicht, warum Du das Rad neu erfinden willst. Unterm Strich wirst Du sehr viele Funktionen benötigen, die in einem RDBMS schon vorhanden sind...



  • So, nun hab ich so viele Hinweise, jetzt werd ich mich mal reinvertiefen. Nochmals danke an alle für die Tips !

    Grüße von sehreilig



  • Hallo,
    nachdem ich etliche Anregungen zu diesem Thema in diesem Forum erhalten habe, möchte ich hier einmal meine Lösung vorstellen, denn ich denke, da gibts vielleicht was zu verbessern 😉

    Die beispiel Struct ist folgende:

    struct Ding
            {
            AnsiString Name;
            int a;
            void write(AnsiString DatName, int i);
            void read(AnsiString DatName, int i);
            }     ;
    

    Mein Problem waren die Methoden read() und write() für den fall, dass ich von der Struct eine Array anlege, etwa so:

    Ding test[5];
    

    Folgende Lösung hab ich nun nach intensiver Nutzung der erhaltenen Hinweise und der Hilfefunktionen gefunden:

    void Ding::write(AnsiString DatName, int i)
    {
     int oFileHandle;
    
     if (i==0)
        oFileHandle = FileCreate(DatName);
     else
         oFileHandle = FileOpen(DatName,fmOpenWrite    ) ;
    
     FileSeek(oFileHandle, 0, 2); //Zeiger ans Ende der datei setzen
    
     int strsize = Name.Length();
     FileWrite(oFileHandle,(char *) &strsize, sizeof(strsize)) ;
    
     FileWrite(oFileHandle, Name.c_str(), strsize +1);
     FileWrite(oFileHandle, (char *) &a, sizeof(a));
    
     FileClose(oFileHandle);
    } ;
    
    void Ding::read(AnsiString DatName,int i) //gehts auch ohne int i ???
    {
     int iFileHandle;
    
     iFileHandle = FileOpen(DatName,fmOpenRead );
    
     int strsize;
     for (int anz =0; anz <=i; anz++)
         {
         FileRead(iFileHandle,(char*) &strsize, sizeof(strsize));
         char *tstr= new char[strsize + 1];
         FileRead(iFileHandle,tstr, strsize+1);
         Name = AnsiString(tstr);
    
         FileRead(iFileHandle,(char*) &a, sizeof(a));
         }
    
     FileClose(iFileHandle);
    
    } ;
    

    Die datei schreiben bzw. lesen geht dann so:

    //Schreiben
    for (int i = 0; i<5; i++)
        {
        test[i].write("daten.txt", i) ;
        }
    
    //Lesen
    for (int i = 0; i<5; i++)
        {
        test2[i].read("daten.txt",i) ;
        ;
        }
    

    😕 Meine Frage: gehts auch ohne den Übergabeparameter int i, mit welchem ich unterscheide, ob das erste Arrayelement (bei write)geschrieben bzw. welches Arrayelement (bei read) gelesen wird. Im Falle von read ist es so, das er mit jedem neuen Aufruf die datei von Anfang an durchläuft, was bei großen Arrays sehr zeitaufwendig ist.

    Über Hinweise würde ich mich sehr freuen

    Grüße von sehreilig



    • Schreib/Lese-Funktionen sind sehr unflexibel. Statt den cast nach char* solltest du direkt einen zeiger dieses typs (oder void*) der funktion übergeben (natürlich dann auch mit Grössenangabe). Damit kannst dann auch andere Daten problemlos speichern.
    • die Variable i würde ich in die Klasse nehmen und dort bool IsOpen nennen. Diese setzt du beim Öffnen der Datei auf true sonst ist sie false.
    • die Datei öffnest du mit eine separaten funktion Open damit du die nicht immer neu öffnen musst vor jeder schreib/lese-aktion (kostet zeit)
    • das filehandle kannste damit auch in die klasse packen.
    • beim lesen natürlich vorher prüfen, ob die datei auch existiert, dito für schreiben, nur das du dort fragen kannst, ob sie überschrieben werden soll.

    aber vielleicht überdenkst du eher noch mal dein klassendesign und speicherst die daten direkt in der klasse.



  • Hallo,

    Warum nimmst du nicht, wie schon vorgeschlagen die TStream-Variante. Die Datei sollte sowieso außerhalb der read/write-Funktionen geöffnet (und dann auch geschlossen) werden. Wenn du unbedingt die FileOpen etc. Varianten verwenden willst, kannst du ja auch das Filehandle übergeben. So wie du es jetzt hast ist es wirklich recht unflexibel und zudem auch langsam. Das ständige Öffnen und Schließen der Dateien ist nicht so richtig gut.



  • Hallo Sunday, hallo Braunstein,

    vielen Dank für eure Hinweise.
    Ich hab zunächst einmal versucht, einige Tips in meine jetzige Lösung einzuarbeiten:

    struct Ding
            {
            AnsiString Name;
            int a;
            float DM;
            int openWrite (AnsiString DatName, bool neu);
            int openRead (AnsiString DatName);
            void closeDatei( int Handle);
            void write(int oFileHandle);
            void read( int iFileHandle);
    
            }     ;
    

    Es funktioniert auch gut. Allerdings sieht die Verwendung von openWrite,bzw openRead oder closeDatei recht unsauber aus:
    z.B

    int Handle = test[0].openWrite("daten.txt",true);
    

    Es sieht aus, als würde speziell für das Arrayelement test[0] ein Handle geholt. Das Handle gilt allerdings für alle dann folgenden Schreibaktionen.

    int Handle = test[0].openWrite("daten.txt",true);
    for (int i = 0; i<5; i++)
        {
        test[i].write(Handle) ;
        }
    test[0].closeDatei(Handle);
    

    Funktioniert zwar, aber find ich nicht so schön.

    Sunday schrieb:

    aber vielleicht überdenkst du eher noch mal dein klassendesign und speicherst die daten direkt in der klasse.

    Mir scheint tatsächlich jetzt auch, dass im Ansatz bereits die Probleme entstehen.
    Ich werde das Ganze jetzt einmal so angehen:

    1. struct ding {alle Eigenschaften vom Ding};
    2. class DingeVerwaltung {Array mit Ding und alles zum Speichern und Laden}

    ich glaube, dann ist die Lösung sehr viel schöner.

    grüße von sehreilig

    Schon Fertig:

    //--------------------------------------------------------------------------
    struct Ding
          {
            AnsiString Name;
            int a;
            float DM;
             }     ;
    
    class DingeVerwalten
            {
            public:
            Ding Dinge[5];
            AnsiString DatName;
    
            void write();
            void read();
    
            }     ;
    //-----------------------------------------------------------------------------
    
    void DingeVerwalten::write()
    {
    
     int Handle;
     Handle = FileCreate(DatName);
    
     for (int i =0; i<5; i++)
         {
    
         int strsize = Dinge[i].Name.Length();
    
         FileWrite(Handle,(void *) &strsize, sizeof(strsize)) ;
         FileWrite(Handle, Dinge[i].Name.c_str(), strsize +1);
         FileWrite(Handle, (void *) &Dinge[i].a, sizeof(Dinge[i].a));
         FileWrite (Handle, (void *) &Dinge[i].DM, sizeof(Dinge[i].DM));
         }
         FileClose(Handle);
    
    } ;
    
    //-----------------------------------------------------------------------------
    
    void DingeVerwalten::read()
    {
     int Handle = FileOpen(DatName,fmOpenRead );
     int strsize;
     for (int i = 0; i<5; i++)
         {
         FileRead(Handle,(void *) &strsize, sizeof(strsize));
         char *tstr= new char[strsize + 1];
         FileRead(Handle,tstr, strsize+1);
         Dinge[i].Name = AnsiString(tstr);
         FileRead(Handle,(void *) &Dinge[i].a, sizeof(Dinge[i].a));
         FileRead (Handle, (void *) &Dinge[i].DM, sizeof(Dinge[i].DM));
         }
      FileClose(Handle);
    } ;
    

    erstmal ohne Fehler abfangen.

    Über Komentare und weitere Hinweise freue ich mich.



  • Der Ansatz ist schonmal in Ordnung. Nur solltest du das eigentliche Schreiben wieder zurück nach Ding verlegen (sprich: genau das, was in der for-Schleife liegt).

    Dann erzeugst du das Handle, durchläufst die for-Schleife und gibst bei einem Dinge.write Aufruf das Handle mit.

    Das wäre dann die Lösung die auch schon einige Seiten weiter vorne vorgeschlagen wurde.



  • Hallo Dasd,
    danke für deinen Hinweis.
    Jetzt sieht das ganze so aus und funktioniert wirklich gut:

    struct Ding
          {
            AnsiString Name;
            int a;
            float DM;
            void write( int Handle);
            void read (int Handle);
             }     ;
    
    class DingeVerwalten
            {
            public:
            Ding Dinge[5];
            AnsiString DatName;
    
            void write();
            void read();
    
            }     ;
    

    Du und Braunstein bevorzugen wohl allerdings eine TStream-Variante.
    Bei meinen Ausflügen durch die Borland-Hilfe fand ich zu meiner jetzigen Lösung viele Hinweise, so kam es zu dieser Lösung. An der Klassenstruktur wird sich vermutlich mit TStreams nicht viel ändern.

    Wieso sind TStreams besser geeignet? Was meint Sunday damit:

    Schreib/Lese-Funktionen sind sehr unflexibel.

    Über eine Antwort würde ich mich freuen.

    Nochmals allen vielen Dank. Grüße von sehr eilig

    Inzwischen hab ich auch eine Variante mit TStreams erprobt.
    Ich muss sagen: Ich bin jetzt sehr zufrieden, meine anfängliche Problem sind durch die Unterstützung hier im Forum voll gelöst ! Zusätzlich hab ich einiges durch eure Tips dazugelernt, was mir alleine aus dem Erarbeiten aus Büchern oder aus der Hilfe kaum möglich gewesen wäre. SUPER 🙂


Anmelden zum Antworten