【问题标题】:boost::spirit::qi duplicate parsing on the outputboost::spirit::qi 输出重复解析
【发布时间】:2012-12-01 22:34:56
【问题描述】:

我有一个使用 Boost::Spirit 的非常简单的解析器:

rule<std::string::iterator, std::string()> zeroTo255 = (string("25") >> char_('0', '5'))
    | (char_('2') >> char_('0', '4') >> digit)
    | (char_('1') >> repeat[2](digit))
    | (char_('1', '9') >> digit) | digit;

当我尝试解析时

std::string o{"1"};
std::string s;
parse(o.begin(), o.end(), zeroTo255, s);
std::cout << o << ": " << s << std::endl;

我有输出

1: 111

我显然做错了什么,但是什么?

【问题讨论】:

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


    【解决方案1】:

    qi::hold 是一种方法,正如@Andrzej 正确提到的那样

    我认为我有一些可能会有所帮助的观察结果以及更好的解决方案。


    关键是 Spirit 设计时不需要属性的“临时”存储。事实上,它不能真正假设属性是可复制的。这就是这里的原因(想象将所有内容解析为单个 std::vector 并为每个解析器步骤进行复制?)。

    在更本质的层面上,在我看来,这里倒退的不是属性处理,而是解析器表达式本身:它没有说明意图,并导致各种处理数字表示的复杂性...确实不应该。

    我的看法是

    rule<std::string::iterator, std::string()> zeroTo255, alternatively;
    
    alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];
    

    你看:你让 Spirit 解析一个数字,实际上只是验证范围,这就是你最初想要做的。

    让我印象深刻的第二件事是,该规则公开了 std::string 属性,而不是 unsigned char,例如这是为什么?

    假设这是一个有意识的设计决定,您可以通过明智地使用

    来实现它
    • 负前瞻 (!parser) - 不影响属性
    • 正向预测 (&amp;parser) - 不会影响属性
    • 熟悉qi::as_stringqi::rawqi::lexemeqi::no_skip
    • 语义操作(不要依赖自动规则

    以下是对原始规则的最小更改可能会产生的效果

    zeroTo255 = raw [ 
              ("25" >> char_("0-5"))
            | ('2' >> char_("0-4") >> digit)
            | ('1' >> digit >> digit)
            | (char_("1-9") >> digit) 
            | digit
        ];
    

    这与使用qi::hold 的代码的效果大致相同,但没有_hold_ing 属性值的性能缺陷。

    希望这会有所帮助。

    完整示例:http://liveworkspace.org/code/4v4CQW$0

    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace qi = boost::spirit::qi;
    
    int main()
    {
        using namespace qi;
        rule<std::string::iterator, std::string()> zeroTo255, alternatively;
    
        zeroTo255 = raw [ 
                  ("25" >> char_("0-5"))
                | ('2' >> char_("0-4") >> digit)
                | ('1' >> digit >> digit)
                | (char_("1-9") >> digit) 
                | digit
            ];
    
        alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];
    
        for (auto& input : std::vector<std::string> { "255", "249", "178", "30", "4" })
        {
            std::string output;
            std::cout << "zeroTo255:\t" << std::boolalpha 
                      << parse(std::begin(input), std::end(input), zeroTo255, output) 
                      << ": " << output << std::endl;
    
            output.clear();
            std::cout << "alternatively:\t" << std::boolalpha 
                      << parse(std::begin(input), std::end(input), alternatively, output) 
                      << ": " << output << std::endl;
        }
    
    }
    

    输出

    zeroTo255:      true: 255
    alternatively:  true: 255
    zeroTo255:      true: 249
    alternatively:  true: 249
    zeroTo255:      true: 178
    alternatively:  true: 178
    zeroTo255:      true: 30
    alternatively:  true: 30
    zeroTo255:      true: 4
    alternatively:  true: 4
    

    【讨论】:

      【解决方案2】:

      我曾经遇到过类似的问题。这是 Spirit 中替代运算符的特殊工作方式。如果您使用附加指令“hold”,您的示例应该可以工作。

      rule<std::string::iterator, std::string()> zeroTo255 
      = hold[string("25") >> char_('0', '5')]
      | hold[char_('2') >> char_('0', '4') >> digit]
      | hold[char_('1') >> repeat[2](digit)]
      | hold[char_('1', '9') >> digit] | digit;
      

      有关此行为的详细信息,请参阅this thread

      【讨论】:

      • 似乎倒退了,但无论如何...谢谢您的快速回答!
      • @brunonery 我明白你所说的“额外工作”是什么意思。我将在此评论中进行解释,但它变得有点大,所以我将其发布为答案:)
      • 对不起 Andrzej - 你的答案是正确的,但 sehe 的更完整。我给他这个,好吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多