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?
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
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.
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.
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:
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).
-
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
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ą.
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 .
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)
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;
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;
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;
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