Structure-array speichern
-
Ich habe das mal gemacht, allerdings habe ich ein dynamisches array gehabt, da ich während der Laufzeit neue Instanzen gebildet habe.
ansonsten habe ich das array in einer ini file gespeichert, geht aber genauso mit filesave. Du musst nur den array namen beim speichern übergeben, wie bei einer einfachen variablen
-
Vielen Dank für die Antwort. Leider kann ich deine Antwort nicht direkt umsetzen. Könntest du mir vielleicht einen Prinzip-Quelltext senden?
Grüße von eilig
-
Bei solchen Konstrukten habe ich immer Probleme mit den Strings. Bislang konnte ich es nur so lösen, dass ich zunächst die Länge und dann den String selbst abgespeichert habe. Weil die einzelnen structs keine konstante Größe haben.
-
Hallo Dasd,
vielen dank. Das ist auch meine Erfahrung. Ich hab bisher in einer Schleife alle Inhalte des Arrays in eine StringList geschrieben und dann abgespeichert. Ich hoffte aber, dass es da auch eine einfachere Möglichkeit gibt.
Grüße von sehreilig
-
Hallo,
Solange nur dieser eine String in deiner Struct steht, ist die Lösung über eine Stringlist fast noch das einfachste. Wenn du direkt binär in eine Datei schreiben willst musst du natürlich (wie schon gesagt) erst die Länge und dann den String selbst schreiben. Um die Schnittstelle zu vereinfachen würde es sich anbieten read/write Funktionen für deine Strukt zu schreiben (als Member für einen fstream oder TStream).
Vielleicht so (nur für write als Beispiel)struct Ding { AnsiString Name; int Anzahl; // wofür ist das? void write(TStream *out); void read(TStream *in); }; void Ding::write(TStream *out) { int strsize = Name.Length(); out->Write(Name.c_str(), strsize+1); }read überlasse ich dir mal (nur wenn du das so machen willst).
Diese Funktionen kannst du dann problemlos in einer Schleife aufrufen.Ciao
-
Ja, dass sieht schon mal sehr übersichtlich aus.
Vielen Dank !
Noch eine Frage, weil ich bisher noch nicht read und write verwendet habe:Liege ich richtig, dass die Zeilen
void Ding::write(TStream *out) { int strsize = Name.Length(); out->Write(Name.c_str(), strsize+1); }so verändert werden müssen, um den gesamten Inhalt der Structure zu speichern:
void Ding::write(TStream *out) { int strsize = Name.Length(); out->Write(strsize+1, Name.c_str(), Anzahl); }Grüße von sehreilig
-
Nein, du mußt jede Variable einzeln speichern. Was macht Anzahl dort überhaupt?
Die write Funktion muß für den String auch noch verändert werden. Du brauchst ja noch die Länge vorher (hatte ich vergessen).
etwa sovoid Ding::write(TStream *out) { int strsize = Name.Length(); out->Write((char*)&strsize, sizeof(strsize)); out->Write(Name.c_str(), strsize+1); }Zur Vollständigkeit noch einen Ansatz von read
void Ding::read(TStream *in) { int strsize; in->Read((char*)&strsize, sizeof(strsize)); char *tstr = new char[strsize+1]; in->Read(tstr, strsize+1); // hier fehlt jetzt noch was }
-
Hallo Braunstein,
Deine Frage nach der Int-Variablen Anzahl: Dort soll die Anzahl des vorhandenen
Dinges abgelegt werden, welches unter Name benannt ist (zb. bei einer Lagerverwaltung).Ich werde mich, sobald ich Zeit habe, mit den Streams beschäftigen. Deine Hinweise sind auf alle Fälle ein guter Einstieg. Deshalb nochmal vielen Dank.
Meine Lösung werde ich dann hier präsentieren, vielleicht gibts ja noch den einen oder anderen Haken, den ich nicht bemerkt habe.
Es grüßt sehreilig
Uff, damit komm ich nicht klar.
In der BCC Hilfe ist kaum was zu finden. In meinen schlauen Büchern auch nicht.
Jetzt fehlt mir zuallererst ein Tip, wo ich was über TStrings nachlesen kann und wo auch ein paar Beispiele sind.
-
Zu TStrings bzw. TStringList findest du alles Nötige in der BCB-Hilfe. Falls du mit der Hilfe nicht klar kommst, schau mal hier http://www.junix.ch/bcb/help/hilfe.html nach.
-
sehreilig schrieb:
(zb. bei einer Lagerverwaltung).
Falls Du wirklich Artikel und Bestände verwalten willst, solltest Du dich mal mit Datenbanken beschäftigen...
-
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 sehreiligHallo 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 StreamLesen 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.Bint 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.