Delphi 2010: ¿Cómo guardar un registro completo en un archivo?

He definido un registro que tiene muchos campos con diferentes tipos (entero, real, cadena, … más arreglos dynamics en términos de “matriz de …”). Quiero guardarlo como un todo en un archivo y luego poder cargarlo nuevamente en mi progtwig. No quiero pasar guardando el valor de cada campo individualmente. El tipo de archivo (binario o ascii o …) no es importante, siempre que Delphi pueda volver a leerlo en un registro.

¿Tienes alguna sugerencia?

Puede cargar y guardar la memoria de un registro directamente desde y hacia una secuencia, siempre que no utilice matrices dinámicas. Entonces, si usa cadenas, debe hacerlas correctas:

 type TTestRecord = record FMyString : string[20]; end; var rTestRecord: TTestRecord; strm : TMemoryStream; strm.Write(rTestRecord, Sizeof(TTestRecord) ); 

¡Incluso puede cargar o guardar una matriz de registros a la vez!

 type TRecordArray = array of TTestRecord; var ra : TRecordArray; strm.Write(ra[0], SizeOf(TTestRecord) * Length(ra)); 

En caso de que quiera escribir contenido dynamic:

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

Después de eso, puedes leerlo de nuevo:

 strm.Read(iCount, Sizeof(iCount) ); //first read the length SetLength(aArray, iCount); //then alloc mem strm.Read(aArray[0], SizeOf * iCount); //then read content 

Como se prometió aquí, es: http://kblib.googlecode.com

Cuando definió, por ejemplo, grabar como:

 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; 

Puede guardar un registro dynamic completo en la transmisión (como datos binarios) de la siguiente manera:

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

Para volver a cargarlo:

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

No necesita ser un registro, puede hacer lo mismo para cualquier tipo dynamic como:

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

Probado en Delphi 2006/2009 / XE. Licencia: MPL 1.1 / GPL 2.0 / LGPL 3.0 Consulte el archivo léame para obtener información.

Otra opción que funciona muy bien para los registros (Delphi 2010+) es usar la biblioteca SuperObject . Por ejemplo:

 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(SO(sValue)); obj := ctx.AsJson(data); sValue := Obj.AsJson; finally ctx.Free; end; end; 

También probé esto brevemente con una simple matriz dinámica TArray y no tuve problemas para almacenar y cargar los elementos de la matriz.

Además de las respuestas que indican cómo hacerlo, también tenga en cuenta estas:

  1. Debe tener en cuenta que escribir registros en un archivo será específico de la versión de Delphi (por lo general: específico para una serie de versiones de Delphi que comparten el mismo diseño de memoria para los tipos de datos subyacentes).

  2. Solo puede hacer eso si su registro no contiene campos de un tipo administrado. Lo que significa que los campos no pueden ser de estos tipos administrados: cadenas , matrices dinámicas, variantes y tipos de referencia (como punteros , tipos de procedimientos , referencias de métodos , interfaces o clases ) y tipos de archivos, o tipos que contienen esos tipos de gestión. Que básicamente se limita a estos tipos no administrados:

    • A: tipos simples (incluidos bytes, enteros, flotantes, enumeraciones, caracteres y similares)
    • B: cuerdas cortas
    • C: Conjuntos
    • D: matrices estáticas de A, B, C, D y E
    • E: Registros de A, B, C, D y E

En lugar de escribir registros en / desde un archivo, podría ser mejor ir con instancias de clase y convertirlas a / desde JSON, y escribir la cadena JSON equivalente a un archivo y volver a leerla.

Puede usar esta unidad para hacer la conversión JSON para usted (debería funcionar con Delphi 2010 y posterior; funciona con seguridad con Delphi XE y superior) desde esta ubicación .

 unit BaseObject; interface uses DBXJSON, DBXJSONReflect; type TBaseObject = class public { public declarations } class function ObjectToJSON(myObject: T): TJSONValue; class function JSONToObject(json: TJSONValue): T; end; implementation { TBaseObject } class function TBaseObject.JSONToObject(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(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. 

Espero que esto te ayude a obtener una visión general de las cosas.

–jeroen

Otra solución, que funciona desde Delphi 5 hasta XE, está disponible como una unidad OpenSource .

De hecho, implementa:

  • algunas funciones RTTI de bajo nivel para manejar tipos de registros: RecordEquals, RecordSave, RecordSaveLength, RecordLoad ;
  • un objeto TDynArray dedicado, que es un contenedor alrededor de cualquier matriz dinámica, capaz de exponer los métodos similares a TList en cualquier matriz dinámica, incluso con registros, cadenas u otras matrices dinámicas. Puede serializar cualquier matriz dinámica.

La serialización utiliza un formato binario optimizado y puede guardar y cargar cualquier matriz dinámica o de registro como RawByteString .

Usamos esto en nuestro ORM, para almacenar tipos de alto nivel como propiedades de matriz dinámica en un back-end de base de datos. Primer paso para una architecture DB-Sharding .

También puede definir un objeto en lugar de un registro, por lo que puede usar RTTI para guardar su objeto en XML o lo que sea. Si tiene D2010 o XE, puede usar DeHL para serializarlo: Delphi 2010 DeHL Serialization XML y atributo personalizado: ¿cómo funciona?

Pero si “google” puede encontrar otras librerías con RTTI y serialización (con D2007, etc.)

Si tiene cadenas dinámicas o matriz, no puede escribir el registro “como un todo”. En lugar de usar cadenas antiguas de estilo antiguo -25 caracteres, agregaría métodos al registro para poder “transmitir” a sí mismo a una secuencia, o mejor utilizando un descendiente 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; 

Códigos 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; 

El problema con guardar un registro que contiene una matriz dinámica o cadenas reales (u otros tipos “administrados” para el caso) es que no es una gran cantidad de memoria que lo contiene todo, sino más bien como un árbol. Alguien o algo necesita revisar todo y guardarlo en almacenamiento, de alguna manera. Otros lenguajes (Python, por ejemplo) incluyen todo tipo de recursos para transformar la mayoría de los objetos en texto (serializarlo), guardarlo en el disco y volver a cargarlo (deserializarlo).

A pesar de que no existe una solución provista por Embarcadero para Delphi, se puede implementar utilizando el RTTI extendido disponible en Delphi 2010. Existe una implementación lista para usar en la biblioteca DeHL (aquí hay una publicación del blog) , pero puedo No digo mucho sobre la implementación, nunca usé DeHL.

Otra opción es la que desea evitar: serializar manualmente el registro en una TStream; En realidad no es medio difícil. Este es el tipo de código que suelo usar para leer / escribir objetos en una secuencia de archivos:

 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;