2010-12-07 8 views
7

Ich habe folgende Strukturen in Delphi 2009:Wie kann ich ein dynamisches Array in Delphi in einem FileStream speichern?

type 
    IndiReportIndi = record 
    IndiName: string; 
    NameNum: integer; 
    ReportIndiName: string; 
    end; 

var 
    XRefList: array of IndiReportIndi; 

wo XRefList ein dynamisches Array ist.

Ich möchte XRefList in einem FileStream speichern. Wie mache ich das UND alle IndiName und ReportIndiName Strings, so dass sie alle wieder abrufbar sein werden, wenn ich später von diesem FileStream laden?

+1

Seit Dezember 2010 gibt es eine neue [Open Source Unit] (http://blog.synopse.info/post/2011/03/12/TDynArray-and-Record-vergleichen/load/save-using-fast-RTTI) erwägenswert für die Serialisierung von Datensätzen oder dynamischen Arrays (mit viel mehr Funktionen als Serialisierung) - Arbeiten für Delphi 5 bis XE2. –

Antwort

6

Verwendung: http://code.google.com/p/kblib/

type 
    IndiReportIndi = record 
    IndiName: string; 
    NameNum: integer; 
    ReportIndiName: string; 
    end; 

    TXRefList = array of IndiReportIndi; 

var 
    XRefList: TXRefList; 

ganzen XRefList speichern Verwendung zu streamen:

TKBDynamic.WriteTo(lStream, XRefList, TypeInfo(TXRefList)); 

es zu laden zurück:

TKBDynamic.ReadFrom(lStream, XRefList, TypeInfo(TXRefList)); 
+0

Sieht aus wie eine wirklich nette Routine. Danke, dass du es aufgezeigt hast. – lkessler

10
type 
    IndiReportIndi = record 
    IndiName: string; 
    NameNum: integer; 
    ReportIndiName: string; 
    procedure SaveToStream(Stream: TStream); 
    procedure LoadFromStream(Stream: TStream); 
    end; 

type 
    TXRefList = array of IndiReportIndi; 

function LoadString(Stream: TStream): string; 
var 
    N: Integer; 

begin 
    Result:= ''; 
    Stream.ReadBuffer(N, SizeOf(Integer)); 
    if N > 0 then begin 
    SetLength(Result, N); 
// Stream.ReadBuffer(Result[1], N * SizeOf(Char)); 
// fast version - see comment by A.Bouchez 
    Stream.ReadBuffer(Pointer(Result)^, N * SizeOf(Char)); 
    end; 
end; 

procedure SaveString(Stream: TStream; const S: string); 
var 
    N: Integer; 

begin 
    N:= Length(S); 
    Stream.WriteBuffer(N, SizeOf(Integer)); 
    if N > 0 then 
// Stream.WriteBuffer(S[1], N * SizeOf(Char)); 
// fast version - see comment by A.Bouchez 
    Stream.WriteBuffer(Pointer(S)^, N * SizeOf(Char)); 
end; 

procedure IndiReportIndi.LoadFromStream(Stream: TStream); 
var 
    S: string; 

begin 
    IndiName:= LoadString(Stream); 
    Stream.ReadBuffer(NameNum, SizeOf(Integer)); 
    ReportIndiName:= LoadString(Stream); 
end; 

procedure IndiReportIndi.SaveToStream(Stream: TStream); 
begin 
    SaveString(Stream, IndiName); 
    Stream.WriteBuffer(NameNum, SizeOf(Integer)); 
    SaveString(Stream, ReportIndiName); 
end; 

function LoadXRefList(Stream: TStream): TXRefList; 
var 
    N: Integer; 
    I: Integer; 

begin 
    Stream.ReadBuffer(N, SizeOf(Integer)); 
    if N <= 0 then Result:= nil 
    else begin 
    SetLength(Result, N); 
    for I:= 0 to N - 1 do 
     Result[I].LoadFromStream(Stream); 
    end; 
end; 

procedure SaveXRefList(Stream: TStream; const List: TXRefList); 
var 
    N: Integer; 
    I: Integer; 

begin 
    N:= Length(List); 
    Stream.WriteBuffer(N, SizeOf(Integer)); 
    for I:= 0 to N - 1 do 
    List[I].SaveToStream(Stream); 
end; 
+2

Sie hätten anstelle von Ergebnis [1] in LoadString und Pointer (S)^anstelle von S [1] in SaveString den Zeiger (Result)^verwenden können, da ein Aufruf von UniqueString() gespeichert wird. LoadString könnte auch ein bisschen schneller sein, wenn result: = '', dann SetLength (Result, N) wenn N> 0: also wird es keine Speicherumleitung geben (mit einer langsamen Bewegung), sondern eine Zeichenkette finalisieren neue Zuweisung (was schneller ist). –

+0

Verwenden Sie einen TReader/TWriter, um das Schreiben von Standarddatentypen zu vereinfachen. Anstelle von Stream.WriteBuffer (N, SizeOf (Integer)); Sie würden einfach Write.WriteInger (N) und N: = Reader.ReadInteger; schreiben. Sie haben auch Unterstützung für Strings und Varianten, und Code ist viel besser lesbar. Du hast einfach Code dupliziert, den Delphi schon hat. –

+0

@ldsandon: TReader/TWriter-Implementierung hängt von der Delphi-Version ab und kann sich ändern. Es ist nicht wünschenswert, sich auf TReader/TWriter-Methoden zu stützen, um Ihre eigene Datenserialisierung zu implementieren. – kludg

5
var 
    S: TStream; 
    W: TWriter; 
    A: array of IndiReportIndi; 
    E: IndiReportIndi; 
    ... 
begin 
    S := nil; 
    W := nil; 
    try 
    S := TFileStream.Create(...); 
    W := TWriter.Create(S); 
    W.WriteInteger(Length(A)); 
    for E in A do 
    begin 
     W.WriteString(E.IndiName); 
     W.WriteInteger(E.NameNum); 
     W.WriteString(E.ReportIndiName); 
    end; 
    finally 
    W.Free; 
    S.Free 
    end; 
end; 

Um diese Daten, die Sie verwenden, um eine TReader und Read/Readstring zu lesen.

+0

Danke ldsandon. Schön und sauber und einfach. +1. – lkessler

0

Gerade für die Antwort genau zu sein:

Betrachten wir einen Blick auf die TDynArray Wrapper nehmen, die jeder zur Serialisierung der Lage ist, Aufzeichnung in Binär- und auch in/von dynamischen Arrays.

Es gibt viele TList ähnliche Methoden, einschließlich neuer Methoden (wie Hashing oder binäre Suche).

Sehr optimiert für Geschwindigkeit und Festplattennutzung, funktioniert für Delphi 5 bis XE2 - und Open Source.

Siehe auch How to store dynamic arrays in a TList?

0

Getestete Lösung:

procedure TForm1.FormCreate(Sender: TObject); 
VAR Stream: TFileStream; 
    R: IndyRecordIndy; 
begin 
Stream:= TFileStream.Create; 
TRY 
    //put rec in stream 
    Stream.WriteBuffer(R, SizeOf(R)); 

    //restore record from stram 
    Stream.Position := 0; 
    Stream.ReadBuffer(R, Stream.Size); 
FINALLY 
    FreeAndNil(Stream); 
END; 
end 



;