【问题标题】:Reading from a file that has Fixed Length Records从具有固定长度记录的文件中读取
【发布时间】:2013-04-03 14:07:48
【问题描述】:
假设您有一个包含以下内容的文件:
-
文件头包含:a) 第一个空记录号,b) 每条记录的大小,以及 c) 文件可以容纳的最大记录数。
-
记录包含:a) 指示记录是空闲还是占用的字节,b) 固定数量的字段。
当我想读取一条随机记录时,我使用这个函数:
int FixedLengthRecordFile :: read (int numRec, FixedLengthFieldsRecord & rec)
问题是,为了让这个函数工作,我必须事先创建一个FixedLengthFieldsRecord,这意味着指定这条记录所具有的字段数......这不是我想要的。我希望文件处理程序足够“智能”以识别记录有多少个字段,并在读取时动态创建FixedLengthFieldsRecord。
我怎样才能做到这一点?该函数必须返回一个int,因为我将它用作退出代码(错误/成功)。
【问题讨论】:
标签:
c++
file
fixed-length-record
【解决方案1】:
您可以定义 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 默认构造函数将对象初始化为无效状态(例如,添加设置为false 的bool isValid 标志),并添加setData(size_t length, const char* data) 成员函数然后可以由FixedLengthRecordFile::read 函数调用。