5

Boost :: Spiritで、セミコロンまたはオプションのセミコロン付きの改行が続くエントリを解析するにはどうすればよいですか?

入力例。各エントリはintとdoubleです。

12 1.4;
63 13.2
2423 56.4 ; 5 8.1

エントリとそれに続く空白を解析するだけのサンプルコードを次に示します。

#include <iostream>
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace qi = boost::spirit::qi;

typedef std::pair<int, double> Entry;

template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, std::vector<Entry>(), Skipper> {
  MyGrammar() : MyGrammar::base_type(entries) {
    entry = qi::int_ >> qi::double_;
    entries = +entry;
  }
  qi::rule<Iterator, Entry(), Skipper> entry;
  qi::rule<Iterator, std::vector<Entry>(), Skipper> entries;
};

int main() {
  typedef boost::spirit::istream_iterator It;
  std::cin.unsetf(std::ios::skipws);
  It it(std::cin), end;

  MyGrammar<It, qi::space_type> entry_grammar;
  std::vector<Entry> entries;
  if (qi::phrase_parse(it, end, entry_grammar, qi::space, entries)
      && it == end) {
    BOOST_FOREACH(Entry const& entry, entries) {
      std::cout << entry.first << " and " << entry.second << std::endl;
    }
  }
  else {
    std::cerr << "FAIL" << std::endl;
    exit(1);
  }
  return 0;
}

さて、私が望む方法(各エントリの後にセミコロンまたはオプションのセミコロン付きの改行が続く)を解析するために、これを置き換えました:

    entries = +entry;

これで:

 entries = +(entry >> (qi::no_skip[qi::eol] || ';'));

ここで、boost::spirit演算子は次の||ことを意味します:(aの後にオプションのbが続く)またはb。1.4ただし、この例の入力の後にスペースがある場合はエラーになります。

12 1.4
63 13.2

スペースが一致していないのは理にかなっていno_skipますが、解決策を見つけることができませんでした。

4

2 に答える 2

6

これが私の見解です。

  • について知りたいと思うかもしれませんqi::blank(これはqi::spaceを除くqi::eol)。これにより、 の必要がなくなりno_skipます。
  • コア文法は次のようになります。

        entry = qi::int_ >> qi::double_;
        entries = entry % +qi::char_("\n;") >> qi::omit[*qi::space];
    
  • BOOST_SPIRIT_DEBUG を使用して、解析が失敗した場所とその理由 (バックトラッキングなど) を学習します。

出力:

12 and 1.4
63 and 13.2
2423 and 56.4
5 and 8.1

完全なコード:

//#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/include/std_pair.hpp>
namespace qi = boost::spirit::qi;

typedef std::pair<int, double> Entry;

template <typename Iterator, typename Skipper>
struct MyGrammar : qi::grammar<Iterator, std::vector<Entry>(), Skipper> {
    MyGrammar() : MyGrammar::base_type(entries) {
        entry = qi::int_ >> qi::double_;
        entries = 
            entry % +qi::char_("\n;")          // the data
            >> qi::omit[*qi::space] > qi::eoi; // trailing whitespace
        BOOST_SPIRIT_DEBUG_NODE(entry);
        BOOST_SPIRIT_DEBUG_NODE(entries);
    }
    qi::rule<Iterator, Entry(), Skipper> entry;
    qi::rule<Iterator, std::vector<Entry>(), Skipper> entries;
};

int main() {
    typedef boost::spirit::istream_iterator It;
    std::cin.unsetf(std::ios::skipws);
    It it(std::cin), end;

    MyGrammar<It, qi::blank_type> entry_grammar;
    std::vector<Entry> entries;
    if (qi::phrase_parse(it, end, entry_grammar, qi::blank, entries)
            && it == end) {
        BOOST_FOREACH(Entry const& entry, entries) {
            std::cout << entry.first << " and " << entry.second << std::endl;
        }
    }
    else {
        std::cerr << "FAIL" << std::endl;
        exit(1);
    }
    return 0;
}
于 2012-05-20T01:34:24.130 に答える
1

さて、これでうまくいくことがわかりました:

entries = +(entry >> (qi::no_skip[*qi::lit(' ') >> qi::eol] || ';'));

というわけで当面の疑問は解決。

ただし、タブがすぐに来ると失敗し1.4ます

12 1.4
63 13.2

これはより良いでしょうが、コンパイルされません:

entries = +(entry >> (qi::no_skip[*qi::space >> qi::eol] || ';'));

エラー:

error: invalid static_cast from type ‘const std::pair<int, double\
>’ to type ‘int’
于 2012-05-20T00:46:35.900 に答える