I need to read data from a text file where the field lengths and record lengths are fixed. Fields are either zero padded or space padded, always appear in the same order and each record is terminated by a CRLF. The file can have one of three possible record types determined by the first character in the record.
So far I've create a base class for all record types and a child class for each record type.
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;
Then I simply read each line of the file as a string, determine its type from the first character, create the appropriate class instance and call Read
.
The idea is that the Record classes can be used for both reading and writing to a string representation of the record. The Read
procedure needs to break up a string and assign it to public fields.
I have two(or three) questions:
- Is this a good approach to handle this type of file?
- If so, what would your implementation of the
Read
procedure look like? (I've dealt with delimited files but this is my first encounter with fixed length fields) - If not, what approach would you take?
Update
Just thought I'd fill in some of the missing details. These record classes are essentially DTOs (data transfer objects). The fields are declared public and the only methods are for conversion to/from a string. The only data validation on the fields is the compiler's type checking. Fields are converted to string in the required order using TStringBuilder.AppendFormat
. This ensures fields are padded and/or truncated to the proper length.
I went with Rob's suggestion to use Copy
combined with the appropriate StrTo*
for getting data from the string. I've also defined field positions and lengths as class constants, i.e.
const Field1Pos = 1;
const Field1Length = 1;
const Field2Pos = 2;
const Field2Length = 5;
The consts are a little easier to read than "magic numbers" in the calls to Copy
.
Any other suggestions would be appreciated.