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?

21voto

André Punkte 8324

Sie können den Speicher eines Datensatzes direkt in und aus einem Stream laden und speichern, solange Sie keine dynamischen Arrays verwenden. Wenn Sie also Zeichenketten verwenden, müssen Sie diese fest einrichten:

type TTestRecord = record 
  FMyString : string[20]; 
end; 

var 
  rTestRecord: TTestRecord;
  strm : TMemoryStream; 

strm.Write(rTestRecord, Sizeof(TTestRecord) );

Sie können sogar eine Reihe von Datensätzen auf einmal laden oder speichern!

type TRecordArray = array of TTestRecord;

var ra : TRecordArray; 

strm.Write(ra[0], SizeOf(TTestRecord) * Length(ra));

Für den Fall, dass Sie dynamische Inhalte schreiben wollen:

iCount   := Length(aArray);
strm.Write(iCount, Sizeof(iCount) );      //first write our length
strm.Write(aArray[0], SizeOf * iCount);   //then write content

Danach können Sie sie wieder lesen:

strm.Read(iCount, Sizeof(iCount) );       //first read the length
SetLength(aArray, iCount);                //then alloc mem
strm.Read(aArray[0], SizeOf * iCount);    //then read content

12voto

Krystian Bigaj Punkte 1286

Wie versprochen, hier ist sie: https://github.com/KrystianBigaj/kblib

Wenn Sie z.B. einen Datensatz definiert haben als:

TTestRecord = record
  I: Integer;
  D: Double;
  U: UnicodeString;
  W: WideString;
  A: AnsiString;
  Options: TKBDynamicOptions;

  IA: array[0..2] of Integer;

  AI: TIntegerDynArray;
  AD: TDoubleDynArray;
  AU: array of UnicodeString;
  AW: TWideStringDynArray;
  AA: array of AnsiString;

  R: array of TTestRecord; // record contain dynamic array of itself (D2009+)
end;

Sie können den gesamten dynamischen Datensatz als Stream (als Binärdaten) speichern, indem Sie :

TKBDynamic.WriteTo(lStream, lTestRecord, TypeInfo(TTestRecord));

Um sie zurückzuladen:

TKBDynamic.ReadFrom(lStream, lTestRecord, TypeInfo(TTestRecord));

Es muss sich nicht um einen Datensatz handeln, Sie können dies auch für jeden anderen dynamischen Typ tun:

TKBDynamic.WriteTo(lStream, lStr, TypeInfo(UnicodeString));
TKBDynamic.WriteTo(lStream, lInts, TypeInfo(TIntegerDynArray));
TKBDynamic.WriteTo(lStream, lArrayOfTestRecord, TypeInfo(TArrayOfTestRecord)); // TArrayOfTestRecord = array of TTestRecord;

Getestet auf Delphi 2006/2009/XE. Lizenz: MPL 1.1/GPL 2.0/LGPL 3.0 Siehe Readme für Informationen.

4voto

skamradt Punkte 15128

Eine weitere Option, die sehr gut für Datensätze (Delphi 2010+) funktioniert, ist die Verwendung der SuperObjekt Bibliothek. Zum Beispiel:

type
  TData = record
    str: string;
    int: Integer;
    bool: Boolean;
    flt: Double;
  end;
var
  ctx: TSuperRttiContext;
  data: TData;
  obj: ISuperObject;
  sValue : string;
begin
  ctx := TSuperRttiContext.Create;
  try
    sValue := '{str: "foo", int: 123, bool: true, flt: 1.23}';
    data := ctx.AsType<TData>(SO(sValue));
    obj := ctx.AsJson<TData>(data);
    sValue := Obj.AsJson;
  finally
    ctx.Free;
  end;
end;

Ich habe dies auch kurz mit einer einfachen TArray<Integer> dynamisches Array und es gab keine Probleme beim Speichern und Laden der Arrayelemente.

3voto

Zusätzlich zu den Antworten, die angeben, wie Sie dies tun, beachten Sie bitte auch die folgenden Punkte:

  1. Sie müssen sich darüber im Klaren sein, dass das Schreiben von Datensätzen in eine Datei Delphi-versionsspezifisch ist (normalerweise: spezifisch für eine Reihe von Delphi-Versionen, die dasselbe Speicherlayout für die zugrunde liegenden Datentypen verwenden).

  2. Dies ist nur möglich, wenn Ihr Datensatz keine Felder eines verwalteten Typs enthält. Das bedeutet, dass die Felder nicht von diesen verwalteten Typen sein können: Zeichenketten dynamische Arrays, Varianten und Referenztypen (wie Zeiger , Verfahrenstypen , Methodenverweise , Schnittstellen o Klassen ) und Dateitypen, oder Typen, die diese verwalteten Typen enthalten. Das beschränkt sich im Wesentlichen auf diese nicht verwalteten Typen:

    • A: Einfache Typen (einschließlich Bytes, Ganzzahlen, Fließkommazahlen, Aufzählungen, Zeichen und dergleichen)
    • B: Kurze Saiten
    • C: Sets
    • D: Statische Arrays von A, B, C, D und E
    • E: Aufzeichnungen von A, B, C, D und E

Anstatt Datensätze in eine Datei zu schreiben, wäre es vielleicht besser, mit Klasseninstanzen zu arbeiten und sie in JSON zu konvertieren und dann das JSON-String-Äquivalent in eine Datei zu schreiben und wieder einzulesen.

Sie können diese Unit verwenden, um die JSON-Konvertierung für Sie durchzuführen (sollte mit Delphi 2010 und höher funktionieren; funktioniert sicher mit Delphi XE und höher) von dieser Standort dieser Standort .

unit BaseObject;

interface

uses DBXJSON, DBXJSONReflect;

type
  TBaseObject = class
  public
    { public declarations }
    class function ObjectToJSON<T : class>(myObject: T): TJSONValue;
    class function JSONToObject<T : class>(json: TJSONValue): T;
  end;

implementation

{ TBaseObject }

class function TBaseObject.JSONToObject<T>(json: TJSONValue): T;
var
  unm: TJSONUnMarshal;
begin
  if json is TJSONNull then
    exit(nil);
  unm := TJSONUnMarshal.Create;
  try
    exit(T(unm.Unmarshal(json)))
  finally
    unm.Free;
  end;

end;

class function TBaseObject.ObjectToJSON<T>(myObject: T): TJSONValue;
var
  m: TJSONMarshal;
begin

  if Assigned(myObject) then
  begin
    m := TJSONMarshal.Create(TJSONConverter.Create);
    try
      exit(m.Marshal(myObject));
    finally
      m.Free;
    end;
  end
  else
    exit(TJSONNull.Create);

end;

end.

Ich hoffe, dies hilft Ihnen, sich einen Überblick zu verschaffen.

--jeroen

3voto

Arnaud Bouchez Punkte 41521

Eine andere Lösung, die von Delphi 5 bis zu XE funktioniert, ist verfügbar als eine OpenSource-Einheit .

Tatsächlich wird es umgesetzt:

  • einige Low-Level-RTTI-Funktionen zur Behandlung von Datensatztypen: RecordEquals, RecordSave, RecordSaveLength, RecordLoad ;
  • eine engagierte TDynArray Objekt, das ein Wrapper für jedes dynamische Array ist und TList-ähnliche Methoden für jedes dynamische Array bereitstellen kann, auch wenn es Datensätze, Strings oder andere dynamische Arrays enthält. Es ist in der Lage, jedes dynamische Array zu serialisieren.

Die Serialisierung verwendet ein optimiertes Binärformat und ist in der Lage, jeden Datensatz oder dynamisches Array zu speichern und zu laden als RawByteString .

Wir verwenden dies in unserem ORM, um High-Level-Typen wie dynamische Array-Eigenschaften in einem Datenbank-Backend zu speichern. Der erste Schritt zu einem DB-Sharding-Architektur .

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