20 Stimmen

Delphi 2010: Wie speichert man einen ganzen Datensatz in einer Datei?

Ich habe einen Datensatz definiert, der viele Felder mit unterschiedlichen Typen hat (Integer, Real, String, ... plus dynamische Arrays im Sinne von "Array of ..."). Ich möchte den Datensatz als Ganzes in einer Datei speichern und ihn dann wieder in mein Programm laden können. Ich möchte nicht jeden Wert jedes Feldes einzeln speichern. Der Dateityp (binär oder ascii oder ...) ist nicht wichtig, solange Delphi ihn in einen Datensatz zurücklesen kann.

Haben Sie irgendwelche Vorschläge?

2voto

André Punkte 8324

Sie könnten auch ein Objekt anstelle eines Datensatzes definieren, so dass Sie RTTI verwenden können, um Ihr Objekt im XML-Format oder auf andere Weise zu speichern. Wenn Sie D2010 oder XE haben, können Sie DeHL verwenden, um es zu serialisieren: Delphi 2010 DeHL Serialisierung XML und benutzerdefinierte Attribute: wie funktioniert das?

Aber wenn Sie "googeln" können Sie andere Libs mit RTTI und Serialisierung (mit D2007 etc) finden

2voto

Wenn Sie dynamische Strings oder Arrays haben, können Sie den Datensatz nicht "als Ganzes" schreiben. Anstatt Strings im alten Stil mit maximal 25 Zeichen zu verwenden, würde ich dem Datensatz Methoden hinzufügen, um sich selbst in einen Stream "streamen" zu können, oder besser einen TFiler-Abkömmling verwenden:

TMyRec = record
  A: string;
  B: Integer;
  procedure Read(AReader: TReader);
  procedure Writer(AWriter: TWriter);
end;

procedure TMyrec.Read(AReader: TReader);
begin
  A := AReader.ReadString;
  B := AReader.ReadInteger;
end;

0voto

SimaWB Punkte 9147

Codes von delphibasics :

 type
   TCustomer = Record
     name : string[20];
     age  : Integer;
     male : Boolean;
   end;

 var
   myFile   : File of TCustomer;  // A file of customer records
   customer : TCustomer;          // A customer record variable

 begin
   // Try to open the Test.cus binary file for writing to
   AssignFile(myFile, 'Test.cus');
   ReWrite(myFile);

   // Write a couple of customer records to the file
   customer.name := 'Fred Bloggs';
   customer.age  := 21;
   customer.male := true;
   Write(myFile, customer);

   customer.name := 'Jane Turner';
   customer.age  := 45;
   customer.male := false;
   Write(myFile, customer);

   // Close the file
   CloseFile(myFile);

   // Reopen the file in read only mode
   FileMode := fmOpenRead;
   Reset(myFile);

   // Display the file contents
   while not Eof(myFile) do
   begin
     Read(myFile, customer);
     if customer.male
     then ShowMessage('Man with name '+customer.name+
                      ' is '+IntToStr(customer.age))
     else ShowMessage('Lady with name '+customer.name+
                      ' is '+IntToStr(customer.age));
   end;

   // Close the file for the last time
   CloseFile(myFile);
 end;

0voto

Cosmin Prund Punkte 25218

Das Problem beim Speichern eines Datensatzes, der ein dynamisches Array oder echte Zeichenketten (oder andere "verwaltete" Typen) enthält, ist, dass es sich nicht um einen großen Speicherblob handelt, der alles enthält, sondern eher um einen Baum. Irgendjemand oder irgendetwas muss alles durchgehen und irgendwie im Speicher ablegen. Andere Sprachen (z. B. Python) bieten alle möglichen Möglichkeiten, die meisten Objekte in Text umzuwandeln (serialisieren), auf der Festplatte zu speichern und wieder zu laden (deserialisieren).

Auch wenn es für Delphi keine von Embarcadero bereitgestellte Lösung gibt, kann eine solche mit der in Delphi 2010 verfügbaren erweiterten RTTI implementiert werden. Eine vorgefertigte Implementierung ist in der DeHL-Bibliothek verfügbar (hier ist ein Blogbeitrag darüber) - aber ich kann nicht viel über die Implementierung sagen, ich habe DeHL nie benutzt.

Eine andere Möglichkeit ist die, die Sie vermeiden wollen: die manuelle Serialisierung des Datensatzes in einen TStream; das ist eigentlich gar nicht so schwierig. Hier ist die Art von Code, die ich normalerweise zum Lesen/Schreiben von Objekten in einen Dateistrom verwende:

procedure SaveToFile(FileName:string);
var F:TFileStream;
    W:TWriter;
    i:Integer;
begin
  F := TFileStream.Create(FileName, fmCreate);
  try
    W := TWriter.Create(F, 128);
    try
      // For every field that needs saving:
      W.WriteString(SomeStr);
      W.WriteInteger(TheNumber);
      // Dynamic arrays? Save the length first, then save
      // every item. The length is needed when reading.
      W.WriteInteger(Length(DArray));              
      for i:=0 to High(DArray) do
        W.WriteString(DArray[i]);
    finally W.Free;
    end;
  finally F.Free;
  end;
end;

procedure ReadFromFile(FileName:string);
var F:TFileStream;
    R:TReader;
    i,n:Integer;
begin
  F := TFileStream.Create(FileName, fmOpenRead);
  try
    R := TReader.Create(F, 128);
    try
      SomeStr := R.ReadString;
      TheNumber := R.ReadInteger;
      // Reading the dynamic-array. We first get the length:
      n := R.ReadInteger;
      SetLength(DArray, n);
      // And item-by-item
      for i:=0 to n-1 do
        DArray[i] := R.ReadString;
    finally R.Free;
    end;    
  finally F.Free;
  end;
end;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X