Question

J'ai besoin de lire des données à partir d'un fichier texte dans lequel le domaine des longueurs et des longueurs d'enregistrements sont fixes.Les champs sont zéro collier ou un espace rembourré, apparaissent toujours dans le même ordre et chaque enregistrement est terminé par un CRLF.Le fichier peut avoir l'un des trois types d'enregistrement déterminé par le premier caractère de l'enregistrement.

Jusqu'à présent, j'ai créer une classe de base pour tous les types d'enregistrement et un enfant de la classe pour chaque type d'enregistrement.

type
  TRecordBase = class abstract
  public
    // Various common fields...
    function ToString: string; virtual; abstract;
    procedure Read(AString: string); virtual; abstract;
  end;

  TRecordType1 = class(TRecordBase)
  public
    //RecordType1 fields...
    function ToString: string; override;
    procedure Read(AString: string); override;
  end;

  TRecordType2 = class(TRecordBase)
  public
    //RecordType2 fields...
    function ToString: string; override;
    procedure Read(AString: string); override;
  end;

  TRecordType3 = class(TRecordBase)
  public
    //RecordType3 fields...
    function ToString: string; override;
    procedure Read(AString: string); override;
  end;

Ensuite, j'ai simplement lire chaque ligne du fichier sous forme de chaîne, déterminer son type à partir du premier caractère, créer la classe d'instance et d'appel Read.

L'idée est que les classes d'Enregistrement peut être utilisé pour la lecture et l'écriture à une représentation de chaîne de l'enregistrement.L' Read la procédure doit briser une chaîne de caractères et l'affecter à des champs publics.

J'ai deux ou trois questions:

  • Est-ce une bonne approche pour gérer ce type de fichier?
  • Si oui, quelle serait votre mise en œuvre de la Read la procédure ressemble?(J'ai fait affaire avec les fichiers délimités mais c'est ma première rencontre avec des champs de longueur fixe)
  • Si non, quelle approche vous prendre?

Mise à jour

Juste pensé que je devais combler les informations manquantes.Ces classes sont essentiellement des Otd (objets de transfert de données).Les champs sont déclarés public et les seules méthodes sont pour la conversion vers/à partir d'une chaîne.La seule validation des données sur les champs est le compilateur de la vérification de type.Les champs sont convertis à la chaîne dans l'ordre requis à l'aide de TStringBuilder.AppendFormat.Cela garantit champs sont rembourrées et/ou tronquées à la bonne longueur.

Je suis allé avec Rob suggestion pour l'utilisation Copy combiné avec le StrTo* pour obtenir des données de la chaîne.J'ai aussi le champ défini par les positions et longueurs constantes de classe, c'est à dire

const Field1Pos = 1;
const Field1Length = 1;
const Field2Pos = 2;
const Field2Length = 5;

Le consts sont un peu plus facile à lire que les "nombres magiques" dans les appels à Copy.

Toutes les autres suggestions seraient appréciées.

Était-ce utile?

La solution

Je voudrais changer une seule chose:Remplacer la lecture de la procédure avec un constructeur de Lire, quelque chose comme ceci:

TRecordBase = class
public
  constructor CreateFromText(Text:string);virtual;abstract;
end;

TRecordType1 = class(TRecordBase)
public
  constructor CreateFromText(Text:string);override;
end;

En fonction de ce que vous faites avec vos dossiers, cela permettra d'économiser un peu de temps et de rendre le code plus facile à lire:

var s:string; // string from stream or string-list
if s[1] = 'X'then DoSomethingWith(TRecordType1.Create(s));

Ayant pratiquement le constructeur est également utile si le nombre de types d'enregistrement grandit.Vous pouvez faire quelque chose comme ceci:

// Define an class type
type TRecordBaseClass = class of TRecordBase;

// Using Delphi 2010? Use a dictionary to register (FirstChar, TRecordBaseClass) paris
var RecordClassDictionary = TDictionary<char, TRecordBaseClass>;

// Init the dictionary like this:
RecordClassDictionary.Add('1', TRecordType1);
RecordClassDictionary.Add('2', TRecordType2);
RecordClassDictionary.Add('3', TRecordType3);

// And use it like this:
var RecordBaseClass: TRecordBaseClass;
for line in TextToParse do
  if RecordClassDictionary.TryGetValue(line[1], RecordBaseClass) then
     // Read the record, do something with the record
     DoSomethingWithTheRecord(RecordBaseClass.CreateFromText(line))
  else
     raise Exception.Create('Unkown record type.');

Autres conseils

Semble OK pour moi.Pour l'extraction de champs, vous pouvez utiliser le Copy fonction standard.Donner de la chaîne d'entrée, l'index du premier caractère du champ, et le nombre de caractères, et il sera de retour cette portion d'une nouvelle chaîne, que vous pouvez ensuite affecter à une autre variable de chaîne ou de passer à une autre fonction pour plus de conversion, tels que StrToInt.

Si la longueur du champ et de la longueur d'enregistrement sont fixes, j'utiliserais presque oublié des documents, avec une variante de la partie:

TRecord1 = packed record
  A: array[0..10] of char;
end;

TRecord2 = packed record
  B: array[0..20] of Byte;
  C: array[0..5] of Byte;
end;

TRecord3 = packed record
  D: array[0..10] of Byte;
  E: array[0..15] of Byte;
  F: array[0..1] of Byte;
end;


TMyRecord = packed record
  case RecordType: Char of
    '1': (Rec1: TRecord1);
    '2': (Rec2: TRecord2);
    '3': (Rec3: TRecord3);
end;

S := ReadLn;

with TMyRecord(S[1]) do
begin
  ...
end;

Si vous utilisez un Delphi version qui prend en charge record de méthodes que vous pouvez utiliser pour accéder à des domaines aussi bien.

Je pense que votre approche est très élégant.

La seule chose que vous ne spécifiez pas est de savoir comment votre Champs de travail.Car ils sont de longueur fixe, je voudrais envisager de faire les propriétés ainsi, dans la Méthode Set de la propriété vous a pu valider la longueur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top