Delphi 2010: как сохранить целую запись в файл?
Я определил запись, которая имеет множество полей с разными типами (целые, реальные, строковые,... плюс динамические массивы с точки зрения "массива..." ).
Я хочу сохранить его в целом в файл, а затем загрузить его обратно в свою программу. Я не хочу, чтобы сохранить каждое значение поля отдельно.
Тип файла (двоичный или ascii или...) не важен, поскольку Delphi может прочитать его обратно в запись.
Есть ли у вас какие-либо предложения?
Ответы
Ответ 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;