Delphi 2010: comment sauvegarder un disque entier dans un fichier?

j'ai défini un enregistrement qui a beaucoup de champs avec différents types (entier, réel , chaîne de caractères, ... plus tableaux dynamiques en termes de " tableau de ..."). Je veux le sauvegarder dans son ensemble dans un fichier et pouvoir ensuite le charger dans mon programme. Je ne veux pas sauver la valeur de chaque champ individuellement. Le type de fichier (binaire ou ascii ou ...) n'est pas important car Delphi pourrait le relire à un disque.

avez-vous des suggestions?

19
demandé sur Mahm00d 2010-09-29 15:06:08

9 réponses

vous pouvez charger et enregistrer la mémoire d'un enregistrement directement depuis et Vers un flux, tant que vous n'utilisez pas de tableaux dynamiques. Donc, si vous utilisez des chaînes, vous devez les faire réparer:

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

var 
  rTestRecord: TTestRecord;
  strm : TMemoryStream; 

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

Vous pouvez même charger ou d'enregistrer un tableau d'enregistrement à la fois!

type TRecordArray = array of TTestRecord;

var ra : TRecordArray; 

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

Dans le cas où vous souhaitez écrire du contenu dynamique:

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

Après cela, vous pouvez lire:

strm.Read(iCount, Sizeof(iCount) );       //first read the length
SetLength(aArray, iCount);                //then alloc mem
strm.Read(aArray[0], SizeOf * iCount);    //then read content
19
répondu André 2010-09-29 11:50:10

Comme promis, la voici: http://kblib.googlecode.com

lorsque vous définissez par exemple record comme:

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;

vous pouvez enregistrer un enregistrement dynamique entier pour le stream (en tant que données binaires) par :

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

À la charge de:

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

Il a pas besoin d'être un disque, vous pouvez faire de même pour n'importe quel type dynamique de la forme:

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

testé sur Delphi 2006/2009 / XE. Licence: MPL 1.1 / GPL 2.0 / LGPL 3.0 Voir le fichier readme pour plus d'informations.

11
répondu kibab 2010-10-02 14:53:51

une Autre option qui fonctionne très bien pour les enregistrements (Delphi 2010+) est d'utiliser le SuperObject bibliothèque. Par exemple:

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;

j'ai aussi testé cela brièvement avec un simple TArray<Integer> tableau dynamique et il n'a pas de problème de stockage et de chargement des éléments du tableau.

4
répondu skamradt 2010-09-30 17:38:26

en plus des réponses qui indiquent comment vous faites ceci, s'il vous plaît aussi être conscient de ceux-ci:

  1. vous devez être conscient que l'écriture d'enregistrements à un fichier sera Delphi version spécifique (habituellement: spécifique à une série de versions de Delphi qui partagent la même mise en page mémoire pour les types de données sous-jacents).

  2. Vous ne pouvez le faire que si votre dossier ne contient pas de champs d'un type géré. Ce qui signifie que les champs ne peuvent pas être de ceux gérés types:chaînes de caractères, tableaux dynamiques, variantes, et les types de référence (comme les pointeurs,types de procédures,méthode références, interfaces ou classes) et les types de fichiers, ou les types qui contiennent ces gère les types. Qui se limite essentiellement à ces types non gérés:

    • A: types simples (y compris les octets, les entiers, les flotteurs, les énumérations, les tels)
    • B:chaînes Courtes
    • C: Sets
    • d: tableaux statiques de A, B, C, D et E
    • e: enregistrements de A, B, C, D et E

au lieu d'écrire des enregistrements à/à partir d'un fichier, il pourrait être préférable d'aller avec les instances de classe et de les convertir à/à partir de JSON, et d'écrire la chaîne de JSON équivalente à un fichier et de le relire.

Vous pouvez utiliser cette unité pour faire la conversion JSON pour vous (devrait fonctionner avec Delphi 2010 et plus; Fonctionne à coup sûr avec Delphi XE et plus) à partir de .

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.

j'espère que cela vous aidera à obtenir un aperçu des choses.

-- jeroen

3
répondu Jeroen Wiert Pluimers 2010-09-29 13:22:41

une autre solution, allant de Delphi 5 à XE, est disponible comme unité OpenSource.

En fait, il met en place:

  • faible niveau de RTTI fonctions pour la manipulation des types d'enregistrement: RecordEquals, RecordSave, RecordSaveLength, RecordLoad;
  • dédié TDynArray object, qui est un wrapper autour de n'importe quel tableau dynamique, capable d'exposer des méthodes de type TList autour de n'importe quel tableau dynamique, même contenant des enregistrements, des chaînes, ou d'autres tableaux dynamiques. Il est capable de sérialiser dynamique tableau.

la sérialisation utilise un format binaire optimisé, et peut enregistrer et charger n'importe quel enregistrement ou tableau dynamique comme RawByteString.

nous l'utilisons dans notre ORM, pour stocker des types de haut niveau comme les propriétés de dynamic array dans un back-end de base de données. Première étape d'un DB-sharding architecture.

3
répondu Arnaud Bouchez 2011-08-20 18:13:16

vous pouvez aussi définir un objet au lieu d'un enregistrement, de sorte que vous pouvez utiliser RTTI pour enregistrer votre objet en XML ou autre. Si vous avez D2010 ou XE, vous pouvez utiliser DeHL pour le sérialiser: Delphi 2010 DeHL la Sérialisation XML et attribut personnalisé : comment ça marche?

mais si vous "google" vous pouvez trouver d'autres libs avec RTTI et serialization (avec D2007 etc)

2
répondu André 2017-05-23 10:30:33

Si vous avez des chaînes dynamiques ou d'une matrice, vous ne pouvez pas écrire l'enregistrement "dans son ensemble". Au lieu d'utiliser d'anciennes chaînes max style-25 caractères, j'ajouterais des méthodes à l'enregistrement pour pouvoir "streamer" lui-même vers un stream, ou mieux en utilisant un descendant 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;
2
répondu 2010-09-29 12:36:43

Codes de 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;
0
répondu SimaWB 2010-09-29 11:24:53

le problème avec la sauvegarde d'un enregistrement contenant un tableau dynamique ou des chaînes réelles (ou d'autres types "managés" d'ailleurs) est, ce n'est pas un gros blob de mémoire contenant tout, c'est plus comme un arbre. Quelqu'un ou quelque chose a besoin de tout examiner et de le stocker, d'une façon ou d'une autre. D'autres langages (Python par exemple) incluent toutes sortes de possibilités pour transformer la plupart des objets en texte (serialize it), le sauvegarder sur disque et le recharger (deserialize it).

Même si un La solution fournie par amarcadero n'existe pas pour Delphi, une solution peut être implémentée en utilisant la RTTI étendue disponible dans Delphi 2010. Une implémentation prête à l'emploi est disponible dans la bibliothèque DeHL (voici un article de blog à ce sujet) - mais je ne peux pas dire grand chose sur l'implémentation, Je n'ai jamais utilisé DeHL.

une autre option est celle que vous voulez éviter: sérialiser manuellement l'enregistrement à un TStream; ce n'est en fait pas à moitié difficile. Voici le genre de code que j'utilise habituellement pour lire/écrire les objets d'un fichier de flux:

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;
0
répondu Cosmin Prund 2011-02-23 09:51:50