Boost :: spirit无法识别可选表达式

时间:2012-12-14 20:20:47

标签: c++ parsing boost-spirit

我正在学习如何使用boost::spirit,即使使用非常简单的解析器,我也面临着一些问题。我正在尝试构建一个解析器,它接受以冒号分隔的数字列表(仅为0或1)。该列表可以有3或4位数。因此,0:0:01:0:1:0有效,而例如0:00:0:0:0:0则不是。

在下面的代码中,您可以看到我如何使用可选运算符指定第一个数字可能存在与否。但是,它不起作用(序列0:0:0的解析失败)。代码有什么问题吗?我会说这是正确的,但我再次开始学习精神。

#include <boost/spirit/include/qi.hpp>

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

void parse_tuple(const std::string& tuple) {
    using qi::char_;

    auto begin = tuple.begin();
    auto end = tuple.end();

    bool r = qi::parse(begin, end,
            -(char_('0', '1') >> ':') >> 
              char_('0', '1') >> ':' >>
              char_('0', '1') >> ':' >>
              char_('0', '1')
            );
    if (!r || begin != end)
        throw std::runtime_error("wrong format");
}

int main() {
    parse_tuple("0:0:0"); // It fails for this one
    parse_tuple("0:0:0:0");

    try { parse_tuple("0:0"); } catch (...) {
        std::cout << "expected error\n"; }
    try { parse_tuple("0:0:0:0:0"); } catch (...) {
        std::cout << "expected error\n"; }
}

2 个答案:

答案 0 :(得分:1)

下面

bool r = qi::parse(begin, end,
        -(char_('0', '1') >> ':') >> 
          char_('0', '1') >> ':' >>
          char_('0', '1') >> ':' >>
          char_('0', '1')
        );

可选的char_应该是最后一个而不是第一个。 规则是按顺序应用的,因此当您解析“0:0:0”时,代码的第一行(可选的东西)会通过测试,然后您的规则要求遵循3位数,而不是2位数。

在我看来,您应该使用%运算符来匹配列表,如果您解析了3或4个元素,请稍后再检查。

修改 或使用qi::repeat来提高可读性。

答案 1 :(得分:0)

这将是最直接的解决方案:

bool r = qi::parse(begin, end,
        char_("01") > ':' >
        char_("01") > ':' >
        char_("01") > -(':' > char_("01"))
        );

完整示例 http://liveworkspace.org/code/3U0QJW$0

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;

void parse_tuple(const std::string& tuple) {
    using qi::char_;

    auto begin = tuple.begin();
    auto end = tuple.end();

    bool r = qi::parse(begin, end,
            char_("01") > ':' >
            char_("01") > ':' >
            char_("01") > -(':' > char_("01"))
            );
    if (!r || begin != end)
        throw std::runtime_error("wrong format");
}

int main() {
    parse_tuple("0:0:0"); // It fails for this one
    parse_tuple("0:0:0:0");

    try { parse_tuple("0:0"); } catch (...) {
        std::cout << "expected error\n"; }
    try { parse_tuple("0:0:0:0:0"); } catch (...) {
        std::cout << "expected error\n"; }
}