You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 11:55:18