2

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

你能给点建议吗?理想情况下,您能否将类草拟为代码示例。

4

3 回答 3

3

它可以以多种方式设计。

  1. 您可以要求用户为您提供流而不是指定文件名。这将更加通用,并且适用于所有流。

这样你应该有一个std::ifstream&成员变量,虽然你也可以有一个指针类型,但你需要*_stream <<调用任何运算符。

  1. 如果您获取一个文件,您可以在构造函数中构造一个流,如果在析构函数中打开则关闭它
于 2013-01-17T14:03:03.583 回答
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成员(是的,应该是 type 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())

于 2013-01-17T14:04:42.647 回答
2

实际上,除了将文件名提供给 之外,还有另一种方法Parser:您可以将其提供给std::istream. 有趣的是,这种方式std::istream可以使用任何派生类,因此您可以提供它,例如 a std::istringstream,这样可以更轻松地编写单元测试。

class Parser {
public:
    explicit Parser(std::istream& is);

    /**/

private:
    std::istream& _stream;
    /**/
};

接下来,是迭代。has在 C++ 中,a后跟 a不是惯用的getstd::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方法和实际的解析内容留给你;)

于 2013-01-17T14:48:40.403 回答