Предположим, у вас есть файл, который содержит следующее:
Когда я хочу прочитать случайную запись, я использую эту функцию:
int FixedLengthRecordFile :: read (int numRec, FixedLengthFieldsRecord & rec)
Проблема в том, что для того, чтобы эта функция работала, я должен создать FixedLengthFieldsRecord
заранее, и это подразумевает указание количества полей, которые имеет эта запись … что не то, что я хочу. Я хочу, чтобы обработчик файлов был достаточно «умным», чтобы распознавать, сколько полей имеет запись, и создавать FixedLengthFieldsRecord
на лету, как он читает.
Как я могу сделать это? Функция должна возвращать int
потому что я использую его как код выхода (ошибка / успех).
Потенциально вы можете определить класс 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];
}
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
функция.