【问题标题】:Boost.spirit: parsing number char and stringBoost.spirit:解析数字字符和字符串
【发布时间】:2013-07-06 03:38:49
【问题描述】:

我需要解析一行,其中包含一个 unsigned int、要丢弃的字符 X 和一个字符串,所有这些都由一个或多个空格分隔。例如,1234 X abcd

bool a = qi::phrase_parse(first, last,
      uint_[ref(num) = _1] >> lit('X') >> lexeme[+(char_ - ' ')],
      space, parsed_str);

上面的代码解析了这三个部分,但字符串最终包含一个垃圾字符 (�abcd) 并且大小为 5 而不是 4。

我的解析器出了什么问题?为什么字符串中有垃圾?

【问题讨论】:

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


    【解决方案1】:

    您可能没有意识到,在存在语义操作*的情况下,解析器表达式不再具有自动属性传播

    * 文档背景:How Do Rules Propagate Their Attributes?

    您正在使用语义操作来“手动”传播uint_ 解析器的属性:

    [ref(num) = _1]   // this is a Semantic Action
    

    因此,解决此问题的最简单方法是也自动传播 numqi::parseqi::phrase_parse API 的预期方式):

    bool ok = qi::phrase_parse(first, last,               // input iterators
            uint_ >> lit('X') >> lexeme[+(char_ - ' ')],  // parser expr
            space,                                      // skipper
            num, parsed_str);                            // output attributes
    

    或者,解决一些题外话,甚至更简洁:

    bool ok = qi::phrase_parse(first, last,
            uint_ >> 'X' >> lexeme[+graph],
            blank, 
            num, parsed_str);
    

    如您所见,您可以传递多个左值作为输出属性接收者。1, 2

    现场观看demo on Coliru (link)

    发生了很多神奇的事情,这实际上导致了我的经验法则:

    除非万不得已,否则避免在灵气表达中使用语义动作

    我以前也有过这个,在一个专门的答案中:Boost Spirit: "Semantic actions are evil"?

    根据我的经验,使用属性自定义点来调整自动传播几乎总是比放弃自动规则并求助于手动属性处理更干净。


    1 从技术上讲,传播这些属性会发生什么,numparsed_str 将作为一个融合序列'绑定'到整个解析表达式:

    fusion::vector2<unsigned&, std::string&>
    

    以及规则的暴露属性:

    fusion::vector2<unsigned, std::vector<char> >
    

    在分配期间将被“转换”为那个。属性兼容性规则允许这种转换以及许多其他转换。


    2 或者,对两者都使用语义操作:

    bool ok = qi::phrase_parse(first, last,
            (uint_ >> 'X' >> as_string [ lexeme[+graph] ]) 
                [ phx::ref(num) = _1, phx::ref(parsed_str) = _2 ],
            blank);
    

    这里有一些微妙之处:

    • 我们需要as_string 来将属性公开为std::string 而不是std::vector&lt;char&gt;(见上文)

    • 我们需要限定phx::ref(parsed_str),因为即使using boost::phoenix::ref 也不足以消除std::refphx::ref 的歧义:ADL 将拖入std::ref,因为它与parsed_str

    • 对语义操作进行分组以防止部分分配的结果,例如即使输入中可能缺少X,以下内容也会覆盖num

      bool ok = qi::phrase_parse(first, last,
             uint_ [ phx::ref(num) = _1 ] 
          >> 'X' 
          >> as_string [ lexeme[+graph] ] [ phx::ref(parsed_str) = _1 ],
          blank);
      

    如果您避免手动传播属性,所有这些复杂性都可以隐藏在您的视野之外!

    【讨论】:

    • 是否有一个干净的解决方案来强制执行separated by one or more spaces 规则?到目前为止我有这个:uint_ &gt;&gt; +no_skip[' '] &gt;&gt; 'X' &gt;&gt; +no_skip[' '] &gt;&gt; lexeme[+graph]。有比这更好的吗?
    • @Arlen Mmm:有distinct in the Spirit repository。前几天有人让我修正他的语法,我提出了quick and dirty oneliner that might give you an idea too。希望有帮助!
    • 但是,如果空格很重要并且您需要控制它的严格存在,我建议不要使用船长并在任何地方拼出预期的空格。
    猜你喜欢
    • 2014-03-24
    • 1970-01-01
    • 2016-11-03
    • 2021-12-11
    • 2015-01-13
    • 1970-01-01
    • 2010-10-18
    • 2021-12-11
    • 1970-01-01
    相关资源
    最近更新 更多