2010-12-07 43 views
7

J'ai les structures suivantes en Delphi 2009:Comment puis-je enregistrer un tableau dynamique dans un FileStream dans Delphi?

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

var 
    XRefList: array of IndiReportIndi; 

où XRefList est un tableau dynamique.

Je souhaite enregistrer XRefList dans un FileStream. Comment puis-je faire ET inclure toutes les chaînes IndiName et ReportIndiName de sorte qu'ils seront tous récupérables lorsque je chargerai plus tard à partir de ce FileStream?

+1

Depuis Décembre 2010, il y a une nouvelle [unité OpenSource] (http://blog.synopse.info/post/2011/03/12/TDynArray-and-Record-compare/load/save-using-fast-RTTI) vaut la peine de prendre en compte la sérialisation des enregistrements ou des tableaux dynamiques (avec beaucoup plus de fonctionnalités que la sérialisation) - pour Delphi 5 jusqu'à XE2. –

Répondre

6

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

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

    TXRefList = array of IndiReportIndi; 

var 
    XRefList: TXRefList; 

Pour enregistrer toute XRefList pour diffuser l'utilisation:

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

pour le charger en arrière:

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

On dirait une très bonne routine. Merci de l'avoir signalé. – 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

Vous auriez pu utiliser le pointeur (résultat)^au lieu du résultat [1] dans LoadString et le pointeur (S)^au lieu de S [1] dans SaveString, car il enregistrera un appel à UniqueString(). LoadString pourrait aussi être un peu plus rapide si codé avec le résultat: = '' dans tous les cas, alors SetLength (Result, N) si N> 0: donc il n'y aura pas de réallocation de mémoire (avec un mouvement lent) nouvelle allocation (qui est plus rapide). –

+0

Utilisez un TReader/TWriter pour simplifier l'écriture des types de données standard. Au lieu de Stream.WriteBuffer (N, SizeOf (Integer)); vous écrivez simplement Write.WriteInger (N) et N: = Reader.ReadInteger ;. Ils ont également un support pour les chaînes et les variantes, et le code est beaucoup plus lisible. Vous venez de dupliquer le code Delphi alread. –

+0

@ldsandon: L'implémentation de TReader/TWriter dépend de la version de Delphi et peut changer. Il n'est pas souhaitable de s'appuyer sur les méthodes TReader/TWriter pour implémenter votre propre sérialisation de données. – 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; 

Pour lire ces données que vous utilisez un TReader et ReadInteger/ReadString.

+0

Merci ldsandon. Agréable et propre et simple. +1 – lkessler

0

Juste pour que la réponse soit exacte:

Tenir compte de jeter un oeil à l'emballage TDynArray, qui est capable de sérialiser tout enregistrer en binaire, et aussi vers/depuis des tableaux dynamiques.

Il existe beaucoup de méthodes similaires à TList, y compris les nouvelles méthodes (comme le hachage ou la recherche binaire).

Très optimisé pour la vitesse et l'utilisation du disque, fonctionne pour Delphi 5 jusqu'à XE2 - et Open Source.

Voir aussi How to store dynamic arrays in a TList?

0

solution testée:

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 



;