Speichern von Records

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Speichern von Records

Beitrag von Joachim Raap »

Hallo, für mich ergibt sich ein nicht lösbares Problem. Ich möchte Records (mehr als einen) in einer Datei speichern, schaffe es aber nicht.
Auf einer Hilfeseite im WEB hat ich ein Beispiel gefunden (s.u.). Das funktioniert so weit hat aber den Haken, daß durch "ReWrite" der vorhandene Satz immer wieder gelöscht wird (soll er natürlich aber nicht!). Wenn ich "Append" verwende, bekomme ich eine Fehlermeldung - geht wohl nur mit Textdateien...
Was mache ich falsch???

Hier die Hilfeseite:
Record abzuspeichern ist ein bischen komplizierter. Da muss man mit typisierten Dateien arbeiten. Das Problem bei solchen typisierte Dateien ist, dass man damit keine unbegrenzten Strings speichern kann. Man muss diese Strings begrenzen, z.B. Vorname: String[20]In unserem Beispiel definieren wir erst mal einen Record:

type
TDatensatz = record
Name: String[20];
Vorname: String[15];
Telefon: String[15];
end;

Und jetzt deklarieren wir eine globale Variable vom Typ TDatensatz:

var
Form1: TForm1;
Datensatz: TDatensatz;

Nun kommt die Speichern Procedure an die Reihe:

procedure TForm1.Button1Click(Sender: TObject);
var
F: File of TDatensatz;
begin
try
AssignFile(F,'C:Variable.dat');
ReWrite(F);
Write(F,Datensatz);
finally
CloseFile(F);
end;
end;

Danke vielmals.....

Benutzeravatar
theo
Beiträge: 10499
Registriert: Mo 11. Sep 2006, 19:01

Re: Speichern von Records

Beitrag von theo »

Probier mal so beim Anhängen:

Code: Alles auswählen

...
FileMode:=2;
Reset(F);
Seek(F,FileSize(F)); 
...

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Speichern von Records

Beitrag von Warf »

Meines erachtens nach solltest du dich wenn möglich von File of Record fernhalten. Es ist so übervereinfacht das es schon wieder nutzlos ist, weil du damit ausschließlich deine Records in die Datei schreiben kannst, keine anderen Daten.
Es gibt aber eigentlich keine situation in der du das machen möchtest, denn Binärdateien (also Dateien in denen die Daten nicht textuell sondern in Rohform drinstehen) enthalten absolut keine metainformationen die verifiziert werden können (wie z.B. das Schemata von XML dateien um auf korrektheit zu überprüfen). Wenn du eine kaputte Binärdatei ohne metadaten hast, wie sie file of ... erzeugt, dann, wenn die Datei falsch ist merkst du das nicht beim Auslesen, sondern du bekommst einfach nur kappute Werte.

Von daher musst du in Binärdateien eigentlich immer einen Header einbauen. Dieser sollte zumindest eine art Magic Number zur Identifikation des Dateiformats enthalten (also irgendeine Byte folge die du dir ausdenkst als eine art ID für dein eigenes Dateiformat, damit du dieses format von anderen dateien unterscheiden kannst), eine Versionsnummer (da wenn du das record layout änderst, die daten falsch geladen werden würden musst du immer identifizieren können welche version deiner records für die datei verwendet wurden), und zusätzlich ist es noch hilfreich die Anzahl an records reinzuschreiben.

File of ... ist aber nicht dafür gemacht verschiedene Datenformate reinzuschreiben (es ist genau dafür gemacht einen Typen zu schreiben und sonst nix). Von daher um so einen header zu schreiben müsstest du die datei einmal als File of HeaderType öffnen um den Header zu lesen und schreiben und einmal als File of RecordType mit dem Offset des Headertypes um die records zu schreiben.
Und da stell ich mir dann die Frage, warum so kompliziert wenn man einfach einen FileStream benutzen kann:

Code: Alles auswählen

var
  fs: TFileStream;
begin
  fs := TFileStream.Create('Filename', fmCreate); // fmCreate: Erstellt datei neu
  try
    fs.Write(Header, SizeOf(Header));
    for i := 0 to RecordCount do
      fs.Write(MyRecords[i], SizeOf(MyRecords[i]));
  finally
    fs. Free;
  end;
end;
Zum appenden musst du einfach nur ein fs.Seek(soEnd, 0); vor dem Write machen

PS: Aus Wartbarkeits und Fehlerresistenzgründen macht es allerdings m.E. nach immer mehr sinn eine Vernünftige Serialisierung (xml, json, yaml, etc.) zu verwenden, das ist zwar etwas mehr aufwand (zumindest bis extended RTTI da ist), aber es lohnt sich eigentlich immer (außer auf hardware wo geschwindigkeit und oder Speicher ein Problem darstellt)

Joachim Raap
Beiträge: 143
Registriert: Mo 30. Mär 2020, 12:37

Re: Speichern von Records

Beitrag von Joachim Raap »

vielen Dank Warf für die ausführliche Antwort.
Ich habe das Problem in die Rubrik "Anfänger" geschrieben. Tut mir leid, aber ich verstehe Deine Erläuterungen daher (eben Anfänger) nicht wirklich.
Mal sehen, ob ich mit dem Code weiterkomme........
Gruß

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Speichern von Records

Beitrag von Winni »

Hi!

Rewrite löscht ein evtl. vorhandene Datei und legt eine neue an.

Für einen zweites Record brauchst Du reset und seek:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
F: File of TDatensatz;
begin
try
AssignFile(F,'C:Variable.dat');
Reset(F);
seek (F,1) // <-- zweites Record
Write(F,Datensatz);
finally
CloseFile(F);
end;
end;

Dieses setzt voraus, dass Du vorher bereits ein Record bereits geschrieben hast.

Winni

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6216
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Speichern von Records

Beitrag von af0815 »

Schau mal hier nach, es gibt zu dem Thema schon einiges an Threads hier
viewtopic.php?t=1238
viewtopic.php?t=8200
etwas komplizierter
viewtopic.php?t=11896
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten