Speichern von Datensätzen, die ein Mitglied vom Typ Zeichenfolge enthalten, in einer Datei (Delphi, Windows)
Frage
Ich habe einen Datensatz, der ungefähr so aussieht:
type
TNote = record
Title : string;
Note : string;
Index : integer;
end;
Einfach.Der Grund, warum ich mich dafür entschieden habe, die Variablen als Zeichenfolgen festzulegen (im Gegensatz zu einem Array von Zeichen), ist, dass ich keine Ahnung habe, wie lang diese Zeichenfolgen sein werden.Sie können 1 Zeichen, 200 oder 2000 Zeichen lang sein.Wenn ich versuche, den Datensatz in einer Typdatei (Datei von...) zu speichern, beschwert sich der Compiler natürlich, dass ich der Zeichenfolge eine Größe zuweisen muss.Gibt es eine Möglichkeit, dies zu überwinden?Oder eine Möglichkeit, diese Datensätze in einer untypisierten Datei zu speichern und trotzdem eine Art durchsuchbare Möglichkeit beizubehalten?
Bitte weisen Sie mich nicht darauf hin mögliche Lösungen, wenn Sie die Lösung kennen, geben Sie bitte den Code ein.Danke
Lösung
Mit einer typisierten Datei ist das nicht möglich.Versuchen Sie so etwas mit einem TFileStream:
type
TStreamEx = class helper for TStream
public
procedure writeString(const data: string);
function readString: string;
procedure writeInt(data: integer);
function readInt: integer;
end;
function TStreamEx.readString: string;
var
len: integer;
iString: UTF8String;
begin
self.readBuffer(len, 4);
if len > 0 then
begin
setLength(iString, len);
self.ReadBuffer(iString[1], len);
result := string(iString);
end;
end;
procedure TStreamEx.writeString(const data: string);
var
len: cardinal;
oString: UTF8String;
begin
oString := UTF8String(data);
len := length(oString);
self.WriteBuffer(len, 4);
if len > 0 then
self.WriteBuffer(oString[1], len);
end;
function TStreamEx.readInt: integer;
begin
self.readBuffer(result, 4);
end;
procedure TStreamEx.writeInt(data: integer);
begin
self.WriteBuffer(data, 4);
end;
type
TNote = record
Title : string;
Note : string;
Index : integer;
procedure Save(stream: TStream);
end;
procedure TNote.Save(stream: TStream);
var
temp: TMemoryStream;
begin
temp := TMemoryStream.Create;
try
temp.writeString(Title);
temp.writeString(Note);
temp.writeInt(Index);
temp.seek(0, soFromBeginning);
stream.writeInt(temp.size);
stream.copyFrom(temp, temp.size);
finally
temp.Free;
end;
end;
Den Ladevorgang überlasse ich Ihnen.Dieselbe Grundidee, aber es sollte kein temporärer Stream erforderlich sein.Mit der Datensatzgröße vor jedem Eintrag können Sie ihn lesen und wissen, wie weit Sie überspringen müssen, wenn Sie nach einer bestimmten Datensatznummer suchen, anstatt den gesamten Eintrag zu lesen.
BEARBEITEN:Dies wurde speziell für Versionen von Delphi geschrieben, die Unicode-Strings verwenden.Bei älteren Versionen könnte man es deutlich vereinfachen.
Andere Tipps
Warum schreiben Sie das nicht als XML?Sehen Sie sich meine Sitzung an "Praktisches XML mit Delphi„wie man damit anfängt.
Eine andere Möglichkeit wäre, Ihre Datensätze in von TComponent absteigende Klassen umzuwandeln und Ihre Daten in DFM-Dateien zu speichern/abzurufen.
Dieser Stackoverflow-Eintrag zeigt Ihnen, wie das geht.
--jeroen
PS:Tut mir leid, meine XML-Antwort war etwas dicht.Ich bin tatsächlich für zwei Konferenzen unterwegs (BASTA! Und DelphiLive!Deutschland).
Im Grunde ist das, was Sie tun müssen, ganz einfach:Erstellen Sie eine Beispiel-XML-Datei und starten Sie dann die Delphi XML-Datenbindungsassistent (verfügbar in Delphi seit Version 6).
Dieser Assistent generiert für Sie eine Einheit mit den Schnittstellen und Klassen, die XML auf Delphi-Objekte abbilden, sowie einigen Hilfsfunktionen zum Lesen aus einer Datei, zum Erstellen eines neuen Objekts usw.Meine Sitzung (siehe den ersten Link oben) enthält tatsächlich die meisten Details für diesen Prozess.
Der obige Link ist ein Video, das die Verwendung des Delphi XML Data Binding Wizard demonstriert.
Sie könnten mit zwei verschiedenen Dateien arbeiten, von denen eine nur die Zeichenfolgen auf praktische Weise speichert und die andere die Datensätze mit einem Verweis auf die Zeichenfolgen speichert.Auf diese Weise verfügen Sie immer noch über eine Datei mit Datensätzen, auf die Sie leicht zugreifen können, auch wenn Sie die Größe des tatsächlichen Inhalts nicht kennen.
(Leider kein Code.)
TNote = record
Title : string;
Note : string;
Index : integer;
end;
könnte übersetzt werden als
TNote = record
Title : string[255];
Note : string[255];
Index : integer;
end;
und verwenden Sie Stream.writebuffer(ANodeVariable, sizeof(TNode), aber Sie sagten, dass Zeichenfolgen in diesem Fall über 255 Zeichen hinausgehen. WENN eine Zeichenfolge über 65535 Zeichen hinausgeht, ändern Sie WORD in INTEGER
type
TNodeHeader=Record
TitleLen,
NoteLen: Word;
end;
(* this is for writing a TNode *)
procedure saveNodetoStream(theNode: TNode; AStream: TStream);
var
header: TNodeHeader;
pStr: PChar;
begin
...
(* writing to AStream which should be initialized before this *)
Header.TitleLen := Length(theNode.Title);
header.NodeLen := Length(theNode.Note);
AStream.WriteBuffer(Header, sizeof(TNodeHeader);
(* save strings *)
PStr := PChar(theNode.Title);
AStream.writeBuffer(PStr^, Header.TitleLen);
PStr := PChar(theNode.Note);
AStream.writebuffer(PStr^, Header.NoteLen);
(* save index *)
AStream.writebuffer(theNode.Index, sizeof(Integer));
end;
(* this is for reading a TNode *)
function readNode(AStream: TStream): TNode;
var
header: THeader
PStr: PChar;
begin
AStream.ReadBuffer(Header, sizeof(TNodeHeader);
SetLength(Result.Title, Header.TitleLen);
PStr := PChar(Result.Title);
AStream.ReadBuffer(PStr^, Header.TitleLen);
SetLength(Result.Note, Header.NoteLen);
PStr := PChar(Result.Note);
AStream.ReadBuffer(PStr^, Header.NoteLen);
AStream.ReadBuffer(REsult.Index, sizeof(Integer)(* 4 bytes *);
end;
Sie können die Funktionen nutzen in dieser Open-Source-Einheit verfügbar.
Es ermöglicht Ihnen, jeden Datensatzinhalt in Binärformate zu serialisieren, einschließlich dynamischer Arrays in:
type
TNote = record
Title : string;
Note : string;
Index : integer;
end;
var
aSave: TRawByteString;
aNote, aNew: TNote;
begin
// create some content
aNote.Title := 'Title';
aNote.Note := 'Note';
aNote.Index := 10;
// serialize the content
aSave := RecordSave(aNote,TypeInfo(TNote));
// unserialize the content
RecordLoad(aNew,pointer(aSave),TypeInfo(TNote));
// check the content
assert(aNew.Title = 'Title');
assert(aNew.Note = 'Note');
assert(aNew.Index = 10);
end;