无法在C ++中实现逐行文件解析器

时间:2013-01-17 13:57:47

标签: c++ parsing file-io c++11

我无法在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))?

你能提一些建议吗?理想情况下,您是否可以将该类描述为代码示例。

3 个答案:

答案 0 :(得分:3)

它可以通过多种方式设计。

  1. 您可以要求用户为您提供流而不是指定文件名。 这将更通用,并将适用于所有流。
  2. 这样你应该有一个std::ifstream&成员变量,虽然你也可以有一个指针类型,但是你需要*_stream <<来调用任何操作符。

    1. 如果你拿一个文件,你可以在构造函数中构建一个流,如果在析构函数中打开则关闭它

答案 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方法和实际解析内容留给您;)