Boost Spirit Qi解析逗号分隔列表的问题求助
问题描述
我正在用Boost Spirit Qi解析可选为空的逗号分隔列表,格式为"a b c, a b c,...",目标是解析为std::vector。目前解析逻辑基本可用,但存在两个问题:
- 空字符串(或全空格字符串)无法解析为空列表;
- 末尾带有逗号的字符串会被判定为解析成功(不符合预期,本应无效)。
当前使用的列表解析规则:
abc_rule >> *( ',' >> abc_rule )
我疑惑为何该规则允许末尾单独存在逗号——按规则逗号后应匹配abc_rule才对,难道是解析不够贪婪?另外,尝试在规则前添加omit[*qi::space] |以支持空列表的操作并未生效。
相关代码:
#include <boost/spirit/include/qi.hpp> #include <iomanip> namespace qi = boost::spirit::qi; struct Out { std::string a, b, c; }; BOOST_FUSION_ADAPT_STRUCT( Out, a, b, c ) int main() { using It = std::string_view::const_iterator; qi::rule<It, std::string()> identifier_rule = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" ); #if 1 qi::rule<It, Out()> abc_rule // = qi::omit[*qi::space] >> identifier_rule // >> qi::omit[+qi::space] >> identifier_rule // >> qi::omit[+qi::space] >> identifier_rule // >> qi::omit[*qi::space]; #else qi::rule<It, Out()> abc_rule = qi::skip( qi::space )[identifier_rule >> identifier_rule >> identifier_rule]; #endif qi::rule<It, std::vector<Out>()> abc_list_rule = abc_rule >> *( ',' >> abc_rule ); for( std::string_view test : { "a0 b0 c0,a1 b1 c1", // valid "a0 b0 c0,", // !!!should be invalid - due to comma at end "XXXX", // invalid "a0 b0 c0", // valid "a0", // invalid " ", // !!!should be valid -> empty list " , ", // invalid } ) { std::cout << "Parsing: [" << test << "]\n"; It f = test.begin(), l = test.end(); if( std::vector<Out> v; qi::parse( f, l, abc_list_rule, v ) ) { std::cout << "Parsed:\n"; std::string left( f, l ); if( !left.empty() ) std::cout << "left : [" << std::string( f, l ) << "]\n "; for( auto& o : v ) { std::cout << "\nA: " << std::quoted( o.a ) << "\n" << "B: " << std::quoted( o.b ) << "\n" << "C: " << std::quoted( o.c ) << "\n"; } } else std::cout << "Failed to parse " << std::quoted( test ) << " left: [" << std::string( f, l ) << "]\n"; } }
解决方案
问题2:末尾逗号被允许的原因及修复
你看到末尾带逗号的字符串被“成功解析”,本质是误解了qi::parse的行为:qi::parse只要规则匹配了输入的前缀部分就会返回true,不会强制解析到输入末尾。比如解析"a0 b0 c0,"时,规则匹配了前面的a0 b0 c0,剩下的,未被处理,但qi::parse依然返回true。
修复方式有两种:
- 强制解析到输入末尾:在规则末尾添加
qi::eoi(输入结束标记),确保整个输入都被匹配:qi::rule<It, std::vector<Out>()> abc_list_rule = (abc_rule >> *(',' >> abc_rule)) >> qi::eoi; - 调用时验证迭代器:即使
qi::parse返回true,也要检查迭代器是否到达输入末尾(f == l),只有两者都满足才判定为解析成功。
问题1:支持空列表的修复
原来的规则abc_rule >> *(',' >> abc_rule)要求至少有一个abc_rule匹配,因此空字符串/全空格字符串无法通过。要支持空列表,需将规则改为允许0个或多个列表项,同时兼容分隔符:
- 方式1:使用Spirit内置的
%(列表解析)结合qi::optional:
其中qi::rule<It, std::vector<Out>()> abc_list_rule = -(abc_rule % ',') >> omit[*qi::space] >> qi::eoi;abc_rule % ','等价于abc_rule >> *(',' >> abc_rule),-表示整个列表是可选的,配合omit[*qi::space]可忽略全空格输入。 - 方式2:手动构造规则,明确允许空:
qi::rule<It, std::vector<Out>()> abc_list_rule = omit[*qi::space] >> -(abc_rule >> *(',' >> abc_rule)) >> omit[*qi::space] >> qi::eoi;
修改后的完整代码
#include <boost/spirit/include/qi.hpp> #include <iomanip> namespace qi = boost::spirit::qi; struct Out { std::string a, b, c; }; BOOST_FUSION_ADAPT_STRUCT( Out, a, b, c ) int main() { using It = std::string_view::const_iterator; qi::rule<It, std::string()> identifier_rule = qi::char_( "a-zA-Z_" ) >> *qi::char_( "a-zA-Z0-9_" ); #if 1 qi::rule<It, Out()> abc_rule // = qi::omit[*qi::space] >> identifier_rule // >> qi::omit[+qi::space] >> identifier_rule // >> qi::omit[+qi::space] >> identifier_rule // >> qi::omit[*qi::space]; #else qi::rule<It, Out()> abc_rule = qi::skip( qi::space )[identifier_rule >> identifier_rule >> identifier_rule]; #endif // 修改后的列表规则:支持空列表+强制解析到末尾 qi::rule<It, std::vector<Out>()> abc_list_rule = -(abc_rule % ',') >> omit[*qi::space] >> qi::eoi; for( std::string_view test : { "a0 b0 c0,a1 b1 c1", // valid "a0 b0 c0,", // 现在判定为无效 "XXXX", // invalid "a0 b0 c0", // valid "a0", // invalid " ", // 现在解析为空列表 " , ", // invalid } ) { std::cout << "Parsing: [" << test << "]\n"; It f = test.begin(), l = test.end(); std::vector<Out> v; if( qi::parse( f, l, abc_list_rule, v ) ) { std::cout << "Parsed:\n"; if( v.empty() ) std::cout << " Empty list\n"; for( auto& o : v ) { std::cout << "\nA: " << std::quoted( o.a ) << "\n" << "B: " << std::quoted( o.b ) << "\n" << "C: " << std::quoted( o.c ) << "\n"; } } else { std::string left( f, l ); std::cout << "Failed to parse " << std::quoted( test ) << " left: [" << left << "]\n"; } } }
内容的提问来源于stack exchange,提问作者llm
相关产品推荐
相关产品推荐

