我需要解析一行包含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。
我的解析器出了什么问题?为什么字符串中有垃圾?
答案 0 :(得分:9)
你可能没有意识到的是,在存在语义动作 * 的情况下,解析器表达式停止自动属性传播。< / p>
您正在使用语义操作来“手动”传播uint_
解析器的属性:
[ref(num) = _1] // this is a Semantic Action
因此,解决此问题的最简单方法是自动传播num
(qi::parse
和qi::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
有很多魔法在进行,实际上这导致了我的经验法则:
除非您必须
,否则请避免在Spirit Qi表达式中使用语义操作
我以前对此有所了解,特别是在答案中:Boost Spirit: "Semantic actions are evil"?
根据我的经验,使用属性自定义点调整自动传播比放弃自动规则和使用手动属性处理几乎总是更清晰。
1 在传播这些属性的技术上,num
和parsed_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<char>
(见上文)
我们需要限定phx::ref(parsed_str)
,因为即使using boost::phoenix::ref
也不足以消除歧义std::ref
和phx::ref
:ADL将拖入std::ref
它与parsed_str
的类型名称相同。
将语义动作分组以防止部分分配的结果,例如即使输入中可能缺少num
,以下内容也会覆盖X
:
bool ok = qi::phrase_parse(first, last,
uint_ [ phx::ref(num) = _1 ]
>> 'X'
>> as_string [ lexeme[+graph] ] [ phx::ref(parsed_str) = _1 ],
blank);
如果您避免手动属性传播,所有这些复杂性都可能会从您的视图中隐藏!