提升精神船长问题

时间:2013-06-12 18:44:33

标签: c++ boost boost-spirit boost-spirit-qi

我对提升精神队长有困难。

我需要解析这样的文件:

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

只有在第一个int之后添加'_'时,我能够毫无问题地解析它(感谢stackoverflow;)。

事实上,我认为船长在第一个int之后吃了行尾,所以第一个和第二个(在第二行)看起来只有一个int。我不明白如何保持eol但是吃空间。我找到了使用自定义解析器的示例,例如herehere

我尝试了qi :: blank,自定义解析器,单个规则点亮('') 无论我使用什么船长,空间和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是我的问题,解决我的问题的方法是不使用skipper,但是phrase_parse需要一个,然后我的解析器需要一个。

我现在被困了几个小时...... 我认为这是我误解的事情。

感谢您的帮助。

1 个答案:

答案 0 :(得分:10)

通常,以下指令有助于抑制/切换中间语法中的队长:

  • qi::lexeme [ p ] inhib禁止船长,例如如果你想确保你解析一个没有内部跳过的标识符)

  • qi::raw [ p ]一如既往地解析,包括跳过,但返回匹配的源序列的原始迭代器范围(包括跳过的位置)

  • qi::no_skip [ p ]
    禁止跳过而不预先跳过
  • qi::skip(s) [ p ]
    完全用另一个队长s替换队长(注意你需要在这样的qi::rule<>子句中使用适当声明的skip[]个实例)

其中p是任何解析器表达式。

具体解决方案

正如您所知,您的问题可能是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;