Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Strukturierte Daten in Datei speichern (https://www.delphipraxis.net/2956-strukturierte-daten-datei-speichern.html)

Luckie 15. Feb 2003 20:30


Strukturierte Daten in Datei speichern
 
Folgendes: Ich habe Daten in Form Überschrift und dazu einen Text und das immer abwechselnd. Dies wollte ich jetzt abspeichern.

Nur wie?

Typisierte Datei mit Records ist schlecht, da man da ja nur ShortStrings verwenden kann, der Text kann aber leider größer werden als 255 Zeichen.
Jetzt dachte ich, kuckst du dir doch mal TFileStream an. Gesagt, getan. Also ich weiß nicht ob es damit geht oder nicht. Und wenn es geht, dann wüßte ich nicht wie. Das Problem ist halt, dass die Textlänge sehr variable ist.
Nachtrag: Ein nachträgliches Bearbeiten soll auch möglich sein.

Irgendwelche Ideen, Vorschlage, Lösungen?

jbg 15. Feb 2003 20:35

Vielleicht hilft dir das bei TFileStream:

Delphi-Quellcode:
// schreibt 4 Bytes Stringlänge und danach S
procedure WriteStreamStr(Stream: TStream; const S: String);
var len: Cardinal;
begin
  len := Length(S);
  Stream.Write(len, SizeOf(len));
  if len > 0 then Stream.Write(S[1], len);
end;

// liest was WriteStreamStr geschrieben hat
procedure ReadStreamStr(Stream: TStream; var S: String);
var len: Cardinal;
begin
  Stream.Read(len, SizeOf(len));
  SetLength(S, len);
  if len > 0 then Stream.Read(S[1], len);
end;

// liest was WriteStreamStr geschrieben hat
function ReadStreamStr(Stream: TStream): String;
begin
  ReadStreamStr(Stream, Result);
end;
Damit kannst du Strings mit der theoretischen Länge von 4 GB schreiben und einlesen.

Luckie 15. Feb 2003 20:42

Gut. Ich glaube, das habe ich verstanden.

Aber:
Code:
Titel
TextTextTextTextTextTextTextTextTextText
TextTextTextTextTextTextText
TextTextTextTextTextTextTextTextText
TextTextTextText
Titel
TextTextTextTextText
TextTextText
TextTextTextTextTextText
Titel
TextTextText
TextTextTextText
Titel
TextTextTextTextText
TextTextTextTextText
TextTextText
Woher weiß ich jetzt, wo was anfängt und aufhört?

jbg 15. Feb 2003 20:47

Ich dachte du hast die Daten bereits im Speicher, dann kannst du mit

Delphi-Quellcode:
procedure WriteEntry(Stream: TFileStream; const Entry: TEntry);
begin
  WriteStreamStr(Stream, Entry.Titel);
  WriteStreamStr(Stream, Entry.Lines.Text);
end;

function ReadEntry(Stream: TFileStream): TEntry;
begin
  Result.Titel := ReadStreamStr(Stream);
  Result.Lines.Text := ReadStreamStr(Stream);
end;

Chewie 15. Feb 2003 20:48

Du musst irgendeine Art Trennzeichen einführen denn irgendeine Information darüber, wie die Daten gegliedert sind, brauchst du ja bei erneutem Einlesen. Also Trennzeichen oder Header-Block mit Byte-Anzahl.

Luckie 15. Feb 2003 20:50

Das mit dem Header würde mir am ehesten zusagen.

Wie könnte denn so ein Header aussehen? Der muß ja auch dynamisch sein. Wenn am Tect was geändert wird oder wenn was rausgelöscht wird usw.

jbg 15. Feb 2003 21:00

Du könntest eine Art "Speicherverwaltung" auf Dateibasis bauen.
Delphi-Quellcode:
THeader = packed record
  TitelLen: Integer;
  TextLen: Integer;
  TitelPos: Longword; // Dateiposition des Titels (bei 0: Freier Block)
  TextPos: Longword; // Dateiposition des Textes
  Next: Longword; // Dateiposition des nächsten Headers (bei 0: Letzter Block)
end;
Dadurch dass du den Header von den eigentlichen Daten trennst, kannst du den Titel und Text an einer völlig anderen Stelle in der Datei ablegen. Sollte also der Platz nicht mehr ausreichen, so verlegst du die Daten einfach an eine Stelle, an der genug Platz vorhanden ist, oder vergrößerst die Datei.

Chewie 15. Feb 2003 21:00

Du musst eben irgendwie reinschreiben, wieviel Bytes der folgende Speicherblock enthält, oder, falls es nur-Text-Daten sind, auch die Zeilenanzahl. Also so zum Beispiel:
mit Zeilen:
Code:
5
Titel
TextTextTextText
Text
Text
TextTextText
2
Titel
TextText
4
Titel
TextTextText
Text
Text
TextTextTextText
oder eben mit der Byte-Länge:
Code:
54
Titel
Das ist ein kurzer Text, der 49 Zeichen lang ist.
Hier müsstest du also die Zahl in der ersten Zeile einlesen, einen weiteren Zeilenumruch überspringen und dann die nächsten 54 Bytes (ist nur überschlagen, können im Beispiel auch 53 oder 55 sein) einlesen. Alternativ wären zwei Längenangaben, wenn du Titel und Text trennen willst.

jbg 15. Feb 2003 21:04

Ich glaube Chewie und ich denken an unterschiedliche Intentionen deiner Seits Luckie. Dadurch das du von Typisierten Dateien sprachst, bin ich natürlich in Richtung Datenbank ausgeschert.

Luckie 15. Feb 2003 21:09

@jbg: Ist gar nicht so schlimm. Dadurch ergeben sich eventuell neue Möglichkeiten, wenn man das von zwei unterschiedlichen Seiten angeht. Aber lies dir noch mal durch, was ich genau in meinem ersten Posting geschrieben habe. Bisher gefällt mir deien Idee noch am besten. Die Idee von Chewie sieht mir mehr so nach einer basteln Lösung aus.

jbg 15. Feb 2003 21:18

Bei Chewies Lösung musst du die Datei immer wieder komplett (bzw. ab der Änderungsstelle) neu schreiben. Bei meiner nur die veränderten Daten und ggf. den THeader anpassen.

Christian Seehase 15. Feb 2003 21:47

Moin Luckie,

also ich hab' so etwas noch etwas anders gelöst, nämlich mit der Aufteilung in eine Daten und eine Indexdatei (eben auch "DB like").
In der Indexdatei stehen dann, im Wesentlichen, jeweils der Offset innerhalb der Datendatei, und ein Feld als Flag, das anzeigt, ob der Satz am Offset gültig ist, oder, z.B. gelöscht.

Als Header für die Daten kämen, in Deinem Falle, zwei Felder für die Längen der beiden Felder (Titel und Text) in Frage.

Wird jetzt ein Text/Titel geändert, kannst Du, anhand des gesamt zur Verfügung stehenden Platzes für den Datensatz, entscheiden, ob Du ihn überschreiben kannst (length(Titel)+Length(Text) <= Vorige Länge) oder nicht.
In letzterem Falle wird der Datensatz hinten an die Datendatei angehängt (FileStream.Position := FileStream.Size).
Nun kannst Du in der Indexdatei entweder dem alten Satz den neuen Offset verpassen, und einen neuen Indexsatz mit dem alten Offset und der Markierung "frei oder gelöscht" schreiben, oder aber einen neuen Indexsatz mit dem neuen Offset und auf den alten Indexsatz wird das Flag entsprechend gesetzt.

Durch diese Trennung hast Du den Vorteil, dass Du schnell auf die Sätze zugreifen kannst, ohne Dich mit den Längenfeldern durch die ganze Datei hangeln zu müssen. Gerade bei Texten können die ja grösser ausfallen.
Ausserdem liessen sich bei einer Kennung "frei" ggf. auch Lücken für neue/geänderte Sätze nutzen.

Nachteil: Gelegentlich müsste die Datei komprimiert werden, um den, durch Änderungen nicht mehr genutzten Platz freizugeben.

In der Indexdatei könntest Du dann z.B. auch noch eine ID des Satzes anlegen (Hash Code), oder den Index als verkettete List aufbauen.

Es dürfte, zumindest wenn die Datei grösser wird, wohl schneller gehen die Indexdatei durchzugehen und gezielt einen Satz aus der Datendatei herauszugreifen, als immer die Datendatei durchzulesen.

Luckie 15. Feb 2003 21:55

Hm, mal sehen. Evnetuell hab eich eine recht banale Lösung gefunden. Aber danke für die Diskussion.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:21 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz