c++filefixed-length-record

Reading from a file that has Fixed Length Records


Suppose you've got a file that contains the following:

When I want to read a random record, I use this function:

int FixedLengthRecordFile :: read (int numRec, FixedLengthFieldsRecord & rec) 

The problem is that, in order for this function to work, I must create a FixedLengthFieldsRecord beforehand, and this implies specifying the number of fields that this record has... which is not what I want. I want the file handler to be "smart" enough to recognize how many fields the record has, and to create a FixedLengthFieldsRecord on-the-fly as it reads it.

How can I accomplish this? The function has to return an int because I use it as an exit code (error/success).


Solution

  • Potentially, you could define the FixedLengthFieldsRecord class so that the class itself enforces the correct record length, but allows for an arbitrary number of fields in each record. A possible good way to accomplish this would be to have the CONSTRUCTOR read in the next record from the file.

    The class (minus some necessary error-checking, which you could add) could be written in similar fashion to the following (this class assumes that you read the first line of the file BEFORE creating objects of this class):

    using namespace std;
    class FixedLengthFieldsRecord
    {
    
        public:
    
            FixedLengthFieldsRecord(int const recordLength, istream & s); // Set the length of the record in the constructor
            bool IsEmpty() const;
            int FieldCount() const; // variable number of fields allowed; but LENGTH of record is enforced (see below)
    
            bool IsValidRecord(); // Does the record contain the correct number of bytes?
            string GetField(int const index) const; // This could throw an exception if the record is not valid
    
        protected:
            // Could have sophisticated functions here to replace fields, remove fields, reorder fields, etc.
    
        // This section contains the actual implementation.
        private:
            vector<string> fields; // The vector contains a VARIABLE number of fields
            bool is_empty;
            bool is_valid;
            int const record_length; // This contains the LENGTH of the record; it is set in the constructor and cannot be changed
    
        // The following variable and function store (and access) ALL the records
        static vector<FixedLengthFieldsRecord> records;
        static read(int numRec, FixedLengthFieldsRecord & rec);
    
    }
    
    FixedLengthFieldsRecord::FixedLengthFieldsRecord(int const recordLength_, istream & s)
        : record_length(recordLength)
    {
        // pseudocode provided here
        // this functionality could be factored into other functions that are called by the constructor
    
        is_valid = true;
    
        is_empty = ReadFirstByte(s); // ReadFirstByte (not shown) reads first byte of current line and returns true if it indicates an empty record
    
        if (!is_empty)
        {
            string field;
            int currentRecordLength = 0;
            while (ReadNextField(s, field)) // ReadNextField() returns true if another field was read from the line (i.e., not end-of-line
            {
                currentRecordLength+= field.length();
            }
            if (currentRecordLength != record_length)
            {
                is_valid = false;
            }
            if (currentRecordLength > record_length)
            {
                break;
            }
            if (is_valid)
            {
                fields.push_back(field);
            }
        }
        if (is_valid)
        {
            records.push_back(*this); // not efficient, nor thread safe - deep copy occurs here
        }
    }
    
    bool FixedLengthFieldsRecord::IsEmpty()
    {
        return is_empty();
    }
    
    bool FixedLengthFieldsRecord::IsValidRecord()
    {
        return is_valid;
    }
    
    string FixedLengthFieldsRecord::GetField(int const index)
    {
        if (!is_valid)
        {
            // throw an exception, or return an empty string
        }
        if (index < 0 || index >= fields.size())
        {
            // throw an exception, or return an empty string
        }
        return fields[index];
    }
    
    FixedLengthFieldsRecord::read(int numRec, FixedLengthFieldsRecord & rec)
    {
        if (numRec < 0 || numRec >= records.size())
        {
            // throw an exception, or just return
        }
        rec = records[numRec];
    }