在Spirit.X3中处理带引号与无引号字符串的最优方案
问题描述
需要解析以下格式的内容:
foo: 123 "bar": 456
要求如果标识符带有引号,解析时自动移除引号。尝试使用了如下解析器:
((+x3::alnum) | ('"' >> (+x3::alnum) >> '"'))
但该解析器的动作参数类型是variant<string, string>,希望让解析器将两种情况视为等价,使动作仅接收单个std::string作为参数。
最小复现代码:
#include <boost/spirit/home/x3.hpp> using namespace boost::spirit; // action handlers struct handlers { void create_member(const std::string& str) { } }; // rules static const x3::rule<struct id_obj_mem> obj_mem = "obj_mem"; #define EVENT(e) ([](auto& ctx) { x3::get<handlers>(ctx).e(x3::_attr(ctx)); }) static const auto obj_mem_def = (( ((+x3::alnum) | ('"' >> (+x3::alnum) >> '"')) >> ':' >> x3::lit("123"))[EVENT(create_member)] % ','); BOOST_SPIRIT_DEFINE(obj_mem) // execution int main() { handlers r; std::string str = "foo: 123"; auto first = str.begin(); auto last = str.end(); bool res = phrase_parse( first, last, boost::spirit::x3::with<handlers>(r)[obj_mem_def], boost::spirit::x3::ascii::space); }
解决方法
核心思路是让两个分支的解析结果属性统一为std::string,避免生成variant类型,有两种常用方案:
方案一:使用x3::omit忽略引号属性
通过x3::omit标记引号部分,使其不参与属性值的生成,这样两个分支的输出属性均为纯字符串:
(+x3::alnum | (x3::omit['"'] >> +x3::alnum >> x3::omit['"']))
方案二:给规则显式指定属性类型
定义带属性类型的x3::rule,Spirit会自动将分支的variant<string, string>转换为std::string:
static const x3::rule<struct id_ident, std::string> ident = "ident"; static const auto ident_def = (+x3::alnum | ('"' >> +x3::alnum >> '"')); BOOST_SPIRIT_DEFINE(ident)
修改后的完整代码
#include <boost/spirit/home/x3.hpp> using namespace boost::spirit; // action handlers struct handlers { void create_member(const std::string& str) { } }; // rules static const x3::rule<struct id_obj_mem> obj_mem = "obj_mem"; static const x3::rule<struct id_ident, std::string> ident = "ident"; #define EVENT(e) ([](auto& ctx) { x3::get<handlers>(ctx).e(x3::_attr(ctx)); }) static const auto ident_def = (+x3::alnum | (x3::omit['"'] >> +x3::alnum >> x3::omit['"'])); static const auto obj_mem_def = ((ident >> ':' >> x3::lit("123"))[EVENT(create_member)] % ','); BOOST_SPIRIT_DEFINE(ident, obj_mem) // execution int main() { handlers r; std::string str = "foo: 123, \"bar\": 456"; auto first = str.begin(); auto last = str.end(); bool res = phrase_parse( first, last, boost::spirit::x3::with<handlers>(r)[obj_mem_def], boost::spirit::x3::ascii::space); }
内容的提问来源于stack exchange,提问作者Jean-Michaël Celerier
相关产品推荐
相关产品推荐

