![]() |
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? |
Vielleicht hilft dir das bei TFileStream:
Delphi-Quellcode:
Damit kannst du Strings mit der theoretischen Länge von 4 GB schreiben und einlesen.
// 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; |
Gut. Ich glaube, das habe ich verstanden.
Aber:
Code:
Woher weiß ich jetzt, wo was anfängt und aufhört?
Titel
TextTextTextTextTextTextTextTextTextText TextTextTextTextTextTextText TextTextTextTextTextTextTextTextText TextTextTextText Titel TextTextTextTextText TextTextText TextTextTextTextTextText Titel TextTextText TextTextTextText Titel TextTextTextTextText TextTextTextTextText TextTextText |
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; |
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.
|
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. |
Du könntest eine Art "Speicherverwaltung" auf Dateibasis bauen.
Delphi-Quellcode:
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.
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; |
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:
oder eben mit der Byte-Länge:
5
Titel TextTextTextText Text Text TextTextText 2 Titel TextText 4 Titel TextTextText Text Text TextTextTextText
Code:
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.
54
Titel Das ist ein kurzer Text, der 49 Zeichen lang ist. |
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.
|
@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.
|
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.
|
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. |
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