Подтвердить что ты не робот

Delphi 2010: как сохранить целую запись в файл?

Я определил запись, которая имеет множество полей с разными типами (целые, реальные, строковые,... плюс динамические массивы с точки зрения "массива..." ). Я хочу сохранить его в целом в файл, а затем загрузить его обратно в свою программу. Я не хочу, чтобы сохранить каждое значение поля отдельно. Тип файла (двоичный или ascii или...) не важен, поскольку Delphi может прочитать его обратно в запись.

Есть ли у вас какие-либо предложения?

4b9b3361

Ответ 1

Вы можете загружать и сохранять память записи непосредственно в поток и из потока, если вы не используете динамические массивы. Поэтому, если вы используете строки, вам нужно сделать их исправленными:

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

var 
  rTestRecord: TTestRecord;
  strm : TMemoryStream; 

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

Вы можете даже загрузить или сохранить массив записей сразу!

type TRecordArray = array of TTestRecord;

var ra : TRecordArray; 

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

Если вы хотите написать динамический контент:

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

После этого вы можете прочитать его обратно:

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

Ответ 2

Как и было сказано, это: http://kblib.googlecode.com

Когда вы определили, например, запись как:

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;

Вы можете сохранить целую динамическую запись в поток (в виде двоичных данных):

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

Чтобы загрузить его обратно:

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

Не нужно записывать, вы можете сделать то же самое для любого динамического типа, например:

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

Протестировано на Delphi 2006/2009/XE. Лицензия: MPL 1.1/GPL 2.0/LGPL 3.0 См. Readme для информации.

Ответ 3

Другим вариантом, который отлично подходит для записей (Delphi 2010+), является использование библиотеки SuperObject. Например:

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;

Я также проверил это кратко с помощью простого динамического массива TArray<Integer>, и у него не было проблемы с хранением и загрузкой элементов массива.

Ответ 4

В дополнение к ответам, которые показывают, как вы это делаете, также имейте в виду:

  • Вы должны знать, что запись записей в файл будет связана с версией Delphi (обычно: для серии версий Delphi, которые используют один и тот же макет памяти для базовых типов данных).

  • Вы можете сделать это только в том случае, если ваша запись не содержит полей управляемого типа. Это означает, что поля не могут быть из этих управляемых типов: строки, динамические массивы, варианты и ссылочные типы (например, pointers, процедурный типы, ссылки методов, interfaces или classes), а также типы файлов или типы, содержащие эти типы управления. В основном это ограничивает эти неуправляемые типы:

    • A: Простые типы (включая байты, целые числа, поплавки, перечисления, символы и т.д.)
    • B: Короткие строки
    • C: Устанавливает
    • D: Статические массивы A, B, C, D и E
    • E: записи A, B, C, D и E

Вместо того, чтобы записывать записи в/из файла, было бы лучше пойти с экземплярами класса и преобразовать их в/из JSON, и они будут записывать строку JSON, эквивалентную файлу, и читать его обратно.

Вы можете использовать этот аппарат для преобразования JSON для вас (должен работать с Delphi 2010 и выше; работает с Delphi XE и выше) из this место.

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.

Надеюсь, это поможет вам получить обзор вещей.

- Йерун

Ответ 5

Другое решение, работающее с Delphi 5 до XE, доступно как блок OpenSource.

Фактически, он реализует:

  • некоторые низкоуровневые функции RTTI для обработки типов записей: RecordEquals, RecordSave, RecordSaveLength, RecordLoad;
  • выделенный объект TDynArray, который является оберткой вокруг любого динамического массива, способный подвергать TLIST-подобные методы любому динамическому массиву, даже содержащему записи, строки или другие динамические массивы. Он способен сериализовать любой динамический массив.

Сериализация использует оптимизированный двоичный формат и может сохранять и загружать любую запись или динамический массив как RawByteString.

Мы используем это в нашем ORM для хранения высокоуровневых типов, таких как динамические свойства массива, в базу данных. Первый шаг к Архитектуре DB-Sharding.

Ответ 6

Вы также можете определить объект вместо записи, поэтому вы можете использовать RTTI для сохранения вашего объекта в XML или что-то еще. Если у вас есть D2010 или XE, вы можете использовать DeHL для его сериализации: Delphi 2010 DeHL Сериализация XML и пользовательский атрибут: как это работает?

Но если вы "google", вы можете найти другие библиотеки с RTTI и сериализацией (с D2007 и т.д.)

Ответ 7

Если у вас есть динамические строки или массив, вы не можете записать запись "в целом". Вместо того, чтобы использовать строки с малым строком в стиле "стиль-25 символов", я бы добавил методы к записи, чтобы иметь возможность "потопить" себя в поток или лучше использовать потомок TFiler:

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;

Ответ 8

Коды из 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;

Ответ 9

Проблема сохранения записи, содержащей динамический массив или реальные строки (или другие "управляемые" типы, если на то пошло), это не большой блок памяти, содержащий все, это больше похоже на дерево. Кто-то или что-то нужно пережить все и как-то сохранить его на хранение. Другие языки (например, Python) включают в себя всевозможные возможности для преобразования большинства объектов в текст (сериализуйте его), сохраняйте их на диске и перезагружайте (десериализовать).

Несмотря на то, что решение для Embarcadero для Delphi не существует, можно реализовать расширенный RTTI, доступный в Delphi 2010. Готовая реализация доступна в библиотеке DeHL (здесь сообщение в блоге об этом) - но я не могу сказать много о реализации, я никогда не использовал DeHL.

Другой вариант - это тот, который вы хотите избежать: вручную сериализуйте запись в TStream; Это на самом деле не сложно. Вот такой код, который я обычно использую для чтения/записи объектов в поток файлов:

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;