Delphi 2010: Jak zapisać cały rekord do pliku?

Zdefiniowałem rekord, który ma wiele pól o różnych typach (integer, real, string,... plus tablice dynamiczne w kategoriach " tablica ..."). Chcę zapisać go jako całość do pliku, a następnie móc załadować go z powrotem do mojego programu. Nie chcę przechodzić przez zapisywanie wartości każdego pola indywidualnie. Typu pliku (binary lub ascii lub ...) nie jest ważne, o ile Delphi może odczytać go z powrotem do rekordu.

Masz jakieś sugestie?

Author: Mahm00d, 2010-09-29

9 answers

Można załadować i zapisać pamięć rekordu bezpośrednio do i ze strumienia, o ile nie używa się tablic dynamicznych. Więc jeśli używasz ciągów, musisz je naprawić:

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

var 
  rTestRecord: TTestRecord;
  strm : TMemoryStream; 

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

Możesz nawet załadować lub zapisać tablicę rekordów na raz!

type TRecordArray = array of TTestRecord;

var ra : TRecordArray; 

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

W przypadku, gdy chcesz napisać dynamiczną treść:

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

Potem możesz przeczytać:

strm.Read(iCount, Sizeof(iCount) );       //first read the length
SetLength(aArray, iCount);                //then alloc mem
strm.Read(aArray[0], SizeOf * iCount);    //then read content
 19
Author: André,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-29 11:50:10

Zgodnie z obietnicą tutaj jest: http://kblib.googlecode.com

Gdy zdefiniowałeś na przykład rekord jako:

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;

Możesz zapisać cały rekord dynamiczny do strumienia (jako dane binarne) przez:

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

Aby załadować go z powrotem:

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

To nie musi być rekord, możesz zrobić to samo dla dowolnego typu dynamicznego, takiego jak:

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

Testowane na Delphi 2006/2009 / XE. Licencja: MPL 1.1 / GPL 2.0 / LGPL 3.0 Więcej informacji można znaleźć w readme.

 11
Author: kibab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-02 14:53:51

Inną opcją, która działa bardzo dobrze dla rekordów (Delphi 2010+) jest użycie biblioteki SuperObject . Na przykład:

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;

Testowałem to krótko z prostą tablicą dynamiczną TArray<Integer> i nie było problemu z przechowywaniem i ładowaniem elementów tablicy.

 4
Author: skamradt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-30 17:38:26

Oprócz odpowiedzi, które wskazują, jak to zrobić, należy również pamiętać o tych:

  1. Musisz być świadomy, że zapisywanie rekordów do pliku będzie specyficzne dla wersji Delphi (Zwykle: specyficzne dla serii wersji Delphi, które mają ten sam układ pamięci dla podstawowych typów danych).

  2. Można to zrobić tylko wtedy, gdy rekord nie zawiera pól typu zarządzanego. Co oznacza, że pola nie mogą należeć do tych zarządzanych typów: strings , tablice dynamiczne, warianty i typy odniesienia (jak wskaźniki , typy proceduralne, Bibliografia, interfejsy lub klasy ) i typy plików, lub typy, które zawierają te zarządzane typy. Co zasadniczo ogranicza się do tych niezarządzanych typów:

    • A: typy proste (w tym bajty, liczby całkowite, pływaki, wyliczenia, znaki itp.)
    • B: krótkie struny
    • C: Zestawy
    • D: Static tablice A, B, C, D i E
    • E: zapisy A, B, C, D i E

Zamiast zapisywania rekordów do / Z pliku, może być lepiej użyć instancji klasy i przekonwertować je do / Z JSON, a one zapisują łańcuch JSON równoważny plikowi i odczytują go z powrotem.

Możesz użyć tej jednostki, aby wykonać konwersję JSON dla Ciebie (powinna działać z Delphi 2010 i nowszymi; na pewno działa z Delphi XE i nowszymi) z tej lokalizacji .

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.

I hope pomaga to uzyskać przegląd rzeczy.

--jeroen

 3
Author: Jeroen Wiert Pluimers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-29 13:22:41

Inne rozwiÄ ... zanie, dziaĹ ' ajÄ ... ce od Delphi 5 do XE, jest dostÄ ™ pne jako opensource unit.

W rzeczywistości implementuje:

  • niektóre niskopoziomowe funkcje RTTI do obsługi typów rekordów: RecordEquals, RecordSave, RecordSaveLength, RecordLoad;
  • dedykowany obiekt TDynArray, który jest opakowaniem wokół dowolnej tablicy dynamicznej, mogącym narazić metody podobne do TList na dowolną tablicę dynamiczną, nawet zawierającą rekordy, łańcuchy znaków lub inne tablice dynamiczne. Jest w stanie serializować dowolną tablicę dynamiczną.
Serializacja używa zoptymalizowanego formatu binarnego i jest w stanie zapisać i załadować dowolny rekord lub tablicę dynamiczną jako RawByteString.

Używamy tego w naszym ORM, aby przechowywać typy wysokiego poziomu, takie jak właściwości tablicy dynamicznej w bazie danych. Pierwszy krok do DB-Sharding architecture .

 3
Author: Arnaud Bouchez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-20 18:13:16

Możesz również zdefiniować obiekt zamiast rekordu, więc możesz użyć RTTI, aby zapisać obiekt w XML lub cokolwiek innego. Jeśli masz D2010 lub XE, możesz użyć DeHL, aby go serializować: Delphi 2010 Dehl serializacja XML i Niestandardowy atrybut: jak to działa?

Ale jeśli "google" można znaleźć inne libs z RTTI i serializacji (z D2007 itp)

 2
Author: André,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:33

Jeśli masz dynamiczne ciągi lub tablicę, nie możesz zapisać rekordu "jako całości". Zamiast używać starego stylu-ciągi znaków max 25, dodałbym metody do rekordu, aby móc "streamować" się do strumienia, lub lepiej używając potomka Tfilera:

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;
 2
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-29 12:36:43

Kody z 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;
 0
Author: SimaWB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-29 11:24:53

Problem z zapisaniem rekordu zawierającego tablicę dynamiczną lub prawdziwe ciągi znaków (lub inne "zarządzane" typy w tym zakresie) polega na tym, że nie jest to duża plamka pamięci zawierająca wszystko, jest bardziej jak drzewo. Ktoś lub coś musi przejrzeć wszystko i zachować to w pamięci. Inne języki (na przykład Python) zawierają różnego rodzaju udogodnienia do przekształcania większości obiektów w tekst (serializowanie), zapisywania na dysk i przeładowywania (deserializacja).

Even though a Embarcadero-dostarczone rozwiązanie nie istnieje dla Delphi, można je zaimplementować przy użyciu rozszerzonego RTTI dostępnego w Delphi 2010. Gotowa implementacja jest dostępna w Bibliotece Dehla (tu jest wpis na blogu) - ale niewiele mogę powiedzieć o implementacji, nigdy nie korzystałem z Dehla.

Inną opcją jest ta, której chcesz uniknąć: ręcznie serializuj rekord do strumienia TStream; w rzeczywistości nie jest to w połowie trudne. Oto rodzaj kodu, którego zwykle używam do odczytu / zapisu obiektów do strumień pliku:

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;
 0
Author: Cosmin Prund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-23 09:51:50