Speichern von Datensätzen, die ein Mitglied vom Typ Zeichenfolge enthalten, in einer Datei (Delphi, Windows)

StackOverflow https://stackoverflow.com/questions/1472325

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

War es hilfreich?

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;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top