Чтение из файла с записями фиксированной длины

Предположим, у вас есть файл, который содержит следующее:

  • заголовок файла содержащий: a) номер первой пустой записи, b) размер каждой записи и c) максимальное количество записей, которое может содержать файл.
  • документация содержащий: а) байт, который указывает, свободна ли запись или занята, б) фиксированное количество полей.

Когда я хочу прочитать случайную запись, я использую эту функцию:

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

Проблема в том, что для того, чтобы эта функция работала, я должен создать FixedLengthFieldsRecord заранее, и это подразумевает указание количества полей, которые имеет эта запись … что не то, что я хочу. Я хочу, чтобы обработчик файлов был достаточно «умным», чтобы распознавать, сколько полей имеет запись, и создавать FixedLengthFieldsRecord на лету, как он читает.

Как я могу сделать это? Функция должна возвращать int потому что я использую его как код выхода (ошибка / успех).

0

Решение

Потенциально вы можете определить класс FixedLengthFieldsRecord так, чтобы сам класс обеспечивает правильную длину записи, но допускает произвольное количество полей в каждой записи. Возможный хороший способ сделать это — прочитать CONSTRUCTOR в следующей записи из файла.

Класс (за исключением некоторой необходимой проверки ошибок, которую вы можете добавить) можно записать аналогично следующему (этот класс предполагает, что вы читаете первую строку файла ДО создания объектов этого класса):

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];
}
2

Другие решения

FixedLengthRecordFile следует открыть файл (или лучше взять std::istream в конструкторе), прочитайте заголовок и затем FixedLengthRecordFile::read(...) функция-член может использовать std::istream::seekg а также std::istream::read чтобы получить данные.

Теперь к вашей актуальной проблеме. Почему вы хотите FixedLengthRecordFile::read функция для возврата int пока он берет запись по ссылке? Не подпись как

FixedLengthFieldsRecord FixedLengthRecordFile::read(size_t numRec, int& foo);

быть проще?

Если вы настаиваете на своей оригинальной подписи, сделайте FixedLengthFieldsRecord конструктор по умолчанию инициализирует объект в недопустимое состояние (например, добавить bool isValid флаг, который установлен в false) и добавить setData(size_t length, const char* data) функция-член, которая затем может быть вызвана FixedLengthRecordFile::read функция.

0

По вопросам рекламы [email protected]