7

ブーストスピリットスキッパーに困っています。

そのようなファイルを解析する必要があります:

ROW int
int [int, int]
int [int, int]
...

最初のintの後に「_」を追加した場合にのみ、問題なく解析できます(stackoverflowのおかげです;)。

実際、スキッパーは最初の int の後に行末を食べると思うので、最初と 2 番目 (2 行目) は 1 つの int にしか見えません。eolを保持する方法がわかりませんが、スペースを食べます。hereおよびhereのようなカスタムパーサーを使用する例を見つけました。

qi::blank を試してみた、1 つのルールを持つカスタム パーサー lit(' ') どんなスキッパーを使用しても、スペースと eol は常に食べられます。

私の文法は次のとおりです。

一行:

struct rowType
{
    unsigned int number;
    std::list<unsigned int> list;
};

構造体に格納された完全な問題:

struct problemType
{
    unsigned int ROW;
    std::vector<rowType> rows;
};

行パーサー:

template<typename Iterator>
struct row_parser : qi::grammar<Iterator, rowType(), qi::space_type>
{
    row_parser() : row_parser::base_type(start)
    {

        list  = '[' >> -(qi::int_ % ',') >> ']';
        start = qi::int_ >> list;
    }

    qi::rule<Iterator, rowType(), qi::space_type> start;
    qi::rule<Iterator, std::list<unsigned int>(), qi::space_type> list;
};

および問題のパーサー:

template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::space_type>
{

    problem_parser() : problem_parser::base_type(start)
    {
        using boost::phoenix::bind;
        using qi::lit;

        start = qi::int_ >> lit('_') >> +(row);

        //BOOST_SPIRIT_DEBUG_NODE(start);
    }

    qi::rule<Iterator, problemType(),qi::space_type> start;
    row_parser<Iterator> row;
};

そして、私はそれを次のように使用します:

main() {
static const problem_parser<spirit::multi_pass<base_iterator_type> > p;
...
spirit::qi::phrase_parse(first, last ,
            p,
            qi::space,
            pb);
}

もちろん、qi::space は私の問題であり、私の問題を解決する方法はスキッパーを使用しないことですが、phrase_parse にはスキッパーが必要であり、パーサーにはスキッパーが必要です。

数時間前から立ち往生しています...明らかに誤解していると思います。

ご協力いただきありがとうございます。

4

1 に答える 1

11

一般に、次のディレクティブは、文法の途中でスキッパーを禁止/切り替えるのに役立ちます。

  • qi::lexeme [ p ]
    内部スキップなしで識別子を確実に解析したい場合など、スキッパーを禁止します) -no_skip比較についてはこちらもご覧ください

  • qi::raw [ p ]
    これは、スキップを含め、いつものように解析しますが、一致したソース シーケンスの生のイテレータ範囲 (スキップされた位置を含む) を返します。

  • qi::no_skip [ p ]
    Pre-skip なしでスキップを禁止する (ここで違いを示す最小限の例を作成しました: Boost Spirit lexeme と no_skip )

  • qi::skip(s) [ p ]
    スキッパーを別のスキッパーに完全に置き換えます(そのような句内で適切に宣言されたインスタンスsを使用する必要があることに注意してください)qi::rule<>skip[]

wherepは任意のパーサー式です。

具体的な解決策

あなたの問題は、あなたがすでに知っているように、すべての空白をqi::space食べることかもしれません. あなたの文法のどこが間違っているのか、おそらくわかりません(完全な文法も関連する入力も示していないため)。

したがって、ここに私が書くものがあります。ノート

  • 特定の場所で改行qi::eol明示的に要求するための の使用
  • qi::blankスキッパーとしての使用(含まずeol
  • 簡潔にするために、文法を組み合わせました

コード:

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;

struct rowType {
    unsigned int number;
    std::list<unsigned int> list;
};

struct problemType {
    unsigned int ROW;
    std::vector<rowType> rows;
};

BOOST_FUSION_ADAPT_STRUCT(rowType, (unsigned int, number)(std::list<unsigned int>, list))
BOOST_FUSION_ADAPT_STRUCT(problemType, (unsigned int, ROW)(std::vector<rowType>, rows))

template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::blank_type>
{
    problem_parser() : problem_parser::base_type(problem)
    {
        using namespace qi;
        list    = '[' >> -(int_ % ',') >> ']';
        row     = int_ >> list >> eol;
        problem = "ROW" >> int_ >> eol >> +row;

        BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
    }

    qi::rule<Iterator, problemType()            , qi::blank_type> problem;
    qi::rule<Iterator, rowType()                , qi::blank_type> row;
    qi::rule<Iterator, std::list<unsigned int>(), qi::blank_type> list;
};

int main()
{
    const std::string input = 
        "ROW 1\n"
        "2 [3, 4]\n"
        "5 [6, 7]\n";

    auto f = begin(input), l = end(input);

    problem_parser<std::string::const_iterator> p;
    problemType data;

    bool ok = qi::phrase_parse(f, l, p, qi::blank, data);

    if (ok) std::cout << "success\n";
    else    std::cout << "failed\n";

    if (f!=l)
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}

本当に改行を必要としない場合:

template<typename Iterator>
struct problem_parser : qi::grammar<Iterator,problemType(),qi::space_type>
{
    problem_parser() : problem_parser::base_type(problem)
    {
        using namespace qi;
        list    = '[' >> -(int_ % ',') >> ']';
        row     = int_ >> list;
        problem = "ROW" >> int_ >> +row;

        BOOST_SPIRIT_DEBUG_NODES((problem)(row)(list));
    }

    qi::rule<Iterator, problemType()            , qi::space_type> problem;
    qi::rule<Iterator, rowType()                , qi::space_type> row;
    qi::rule<Iterator, std::list<unsigned int>(), qi::space_type> list;
};

int main()
{
    const std::string input = 
        "ROW 1 " // NOTE whitespace, obviously required!
        "2 [3, 4]"
        "5 [6, 7]";

    auto f = begin(input), l = end(input);

    problem_parser<std::string::const_iterator> p;
    problemType data;

    bool ok = qi::phrase_parse(f, l, p, qi::space, data);

    if (ok) std::cout << "success\n";
    else    std::cout << "failed\n";

    if (f!=l)
        std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}

アップデート

コメントへの応答: ファイルから入力を読み取る方法を示すスニペットを次に示します。これはテスト済みで、私にとってはうまく機能します:

std::ifstream ifs("input.txt"/*, std::ios::binary*/);
ifs.unsetf(std::ios::skipws);

boost::spirit::istream_iterator f(ifs), l;

problem_parser<boost::spirit::istream_iterator> p;
于 2013-06-12T19:44:36.607 に答える