我无法在C ++ 11中实现一个简单的文件解析器,它逐行读取文件并对行进行标记。它应该妥善管理其资源。解析器的用法应该是:
Parser parser;
parser.open("/path/to/file");
std::pair<int> header = parser.getHeader();
while (parser.hasNext()) {
std::vector<int> tokens = parser.getNext();
}
parser.close();
所以Parser
课程需要一名成员std::ifstream file
(或std::ifstream* file
?)
1)构造函数应该如何初始化this->file
?
2)open
方法应该如何将this->file
设置为输入文件?
3)如何将文件中的下一行加载到字符串中?
(这是你要使用的:std::getline(this->file, line)
)?
答案 0 :(得分:3)
它可以通过多种方式设计。
这样你应该有一个std::ifstream&
成员变量,虽然你也可以有一个指针类型,但是你需要*_stream <<
来调用任何操作符。
答案 1 :(得分:3)
由于Parser
在构建它之后和打开文件之前可能处于一种非常无用的状态,我建议你的用例看起来像这样:
Parser parser("/path/to/file");
std::pair<int> header = parser.getHeader();
while (parser.hasNext()) {
std::vector<int> tokens = parser.getNext();
}
parser.close();
在这种情况下,您应该使用构造函数的成员初始化列表来初始化file
成员(是的,应该是类型std::ifstream
):
Parser::Parser(std::string file_name)
: file(file_name)
{
// ...
}
如果您将构造函数和open
成员函数分开,则可以将构造函数保留为默认值,因为file
成员将默认构造,为您提供与任何文件无关的文件流。然后,您将Parser::open
将文件名转发到std::ifstream::open
,如下所示:
void Parser::open(std::string file_name)
{
file.open(file_name);
}
然后,是的,要从文件中读取行,您希望使用与此类似的内容:
std::string line;
while (std::getline(file, line)) {
// Do something with line
}
不要陷入做while (!file.eof())
的陷阱。
答案 2 :(得分:2)
实际上,还有一种方法可以将文件名称提供给Parser
:您可以将其提供给std::istream
。有趣的是,这样可以使用std::istream
的任何派生类,因此可以提供它,例如std::istringstream
,这样可以更容易地编写单元测试。
class Parser {
public:
explicit Parser(std::istream& is);
/**/
private:
std::istream& _stream;
/**/
};
接下来,迭代。在C ++中,has
后跟get
并不是惯用的。 std::istream
支持迭代(使用输入迭代器),您可以完美地设计解析器,因此它也可以。这样您就可以兼容许多STL算法。
class ParserIterator:
public std::iterator< std::input_iterator_tag, std::vector<int> >
{
public:
ParserIterator(): _stream(nullptr) {} // end
ParserIterator(std::istream& is): _stream(&is) { this->advance(); }
// Accessors
std::vector<int> const& operator*() const { return _vec; }
std::vector<int> const* operator->() const { return &_vec; }
bool equals(ParserIterator const& other) const {
if (_stream != other._stream) { return false; }
if (_stream == nullptr) { return true; }
return false;
}
// Modifiers
ParserIterator& operator++() { this->advance(); return *this; }
ParserIterator operator++(int) {
ParserIterator tmp(*this);
this->advance();
return tmp;
}
private:
void advance() {
assert(_stream && "cannot advance an end iterator");
_vec.clear();
std::string buffer;
if (not getline(*_stream, buffer)) {
_stream = 0; // end of story
}
// parse here
}
std::istream* _stream;
std::vector<int> _vec;
}; // class ParserIterator
inline bool operator==(ParserIterator const& left, ParserIterator const& right) {
return left.equals(right);
}
inline bool operator!= (parserIterator const& left, ParserIterator const& right) {
return not left.equals(right);
}
然后我们可以扩充我们的解析器:
ParserIterator Parser::begin() const {
return ParserIterator(_stream);
}
ParserIterator Parser::end() const {
return ParserIterator();
}
我会将getHeader
方法和实际解析内容留给您;)