2

ファイルを 1 行ずつ読み取り、その行をトークン化する単純なファイル パーサーを 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クラスには 1 つのメンバーstd::ifstream file(またはstd::ifstream* file?)が必要です。

1) コンストラクターはどのように初期化する必要がありますthis->fileか?

open2)メソッド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;
    /**/
};

次に、繰り返しです。hasa の後に a が続くことは、C++ では慣用的ではありません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 に答える