Boost.Spirit そのに from stream
http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/tutorials.html は Boost.Spirit.Qi を使って parser を書く上ですばらしい資料なんだけど入力が std::string を想定してるようであんまり広がらない。
というわけで stream から流し込める方法ないのかなーと思って探した結果 2 つ方法があった。
- boost::spirit::qi::match()
- boost::spirit::istream_iterator + boost::spirit::qi::parse()
まず boost::spirit::qi::match() 関数を使う方から。 http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/reference/parse_api/stream_api.html に書いてあるんだけどそのまま stream に流し込める関数みたい。
#include <iostream> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/qi_match.hpp> int main(void) { namespace qi = boost::spirit::qi; std::cin.unsetf(std::ios_base::skipws); int n; std::cin >> qi::match('"' >> qi::int_ >> '"', n); std::cout << n << std::endl; return 0; }
で、もうひとつが boost::spirit::istream_iterator なんだけど名前の通り istream を wrap して iterator として扱えるようにするための class 。 http://boost-spirit.com/home/2010/01/05/stream-based-parsing-made-easy/ に解説が載ってる。
#include <iostream> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/support_istream_iterator.hpp> int main(void) { namespace spirit = boost::spirit; namespace qi = boost::spirit::qi; std::cin.unsetf(std::ios_base::skipws); spirit::istream_iterator begin(std::cin); spirit::istream_iterator end; int n; qi::parse(begin, end, '"' >> qi::int_ >> '"', n); std::cout << n << std::endl; return 0; }
で、どっちがいいの ? ていう疑問が出てくると思うんだけど
- boost::spirit::qi::match() は stream に対してしか使えないけど手軽
- boost::spirit::istream_iterator + boost::spirit::qi::parse() はちょっと code が長くなるけど iterator という間口の広い interface が使える
というあたりを考慮して使いまわすつもりがあるなら parse() を使った code を書いてしまって stream から流しこむ必要があるときに別途 istream_iterator で wrap する、という方向。たとえば http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/tutorials/warming_up.html の下の方の parse_numbers() 関数は iterator を引数にとるので std::string だろうが istream_iterator で wrap した stream だろうが問題ないということね。
それ以外の場合、書き捨てるとか stream からしか流し込まないことがわかってるとかは match() を使えばいいんじゃないかな。