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

Boost::spirit does not recognize an optional expression

本文关键字:表达式 识别 spirit Boost      更新时间:2023-10-16

我正在学习如何使用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 errorn"; }
    try { parse_tuple("0:0:0:0:0"); } catch (...) {
        std::cout << "expected errorn"; }
}

这里

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

可选char_应该是最后一个,而不是第一个。这些规则是按顺序应用的,因此当您解析"0:0:0"时,代码的第一行(可选内容)通过测试,然后您的规则期望后面跟着 3 位数字,而不是两位数字。

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

编辑或者使用 qi::重复以提高可读性。

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

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 errorn"; }
    try { parse_tuple("0:0:0:0:0"); } catch (...) {
        std::cout << "expected errorn"; }
}