使用Boost Spirit Qi解析含空白的标识符串并映射到结构体
问题解决:Boost Spirit Qi 解析时空白符混入结果的问题
问题原因
你的猜想完全正确——*qi::space和+qi::space会生成对应属性(匹配到的空白符序列),当这些规则和返回std::string的identifier_rule通过>>连接时,Spirit会把所有规则的属性按顺序组合成一个属性序列。而abc_rule要适配Out结构体(需要3个std::string属性),所以Spirit会错误地将前三个属性(开头的空白符、第一个标识符、中间的空白符)分别赋值给a、b、c,直接导致断言失败。
两种可行解决方案
方案1:用qi::omit屏蔽空白符的属性
直接把空白符规则包裹在qi::omit[]中,让它们不生成属性,这样最终的属性序列就只剩下三个标识符的结果:
boost::spirit::qi::rule<char const*, 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];
方案2:使用Spirit的自动空白跳过(更简洁)
Spirit支持为规则设置跳过器,自动忽略指定的空白符,这样你不需要手动写空白符规则,代码会简洁很多:
// 为规则设置自动跳过空白符的特性 qi::rule<char const*, std::string(), qi::space_type> identifier_rule = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_"); boost::spirit::qi::rule<char const*, Out(), qi::space_type> abc_rule = identifier_rule >> identifier_rule >> identifier_rule; // 解析时用phrase_parse指定跳过器为qi::space assert(qi::phrase_parse(f, l, abc_rule, qi::space, o));
这里phrase_parse会自动应用qi::space作为跳过器,忽略所有空白符(包括空格、换行、制表符等),你只需要定义核心的标识符序列规则即可。
修改后的完整代码(以方案2为例)
#include <string> #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; struct Out { Out() = default; Out(const std::string& a_, const std::string& b_, const std::string& c_) : a(a_), b(b_), c(c_) {} std::string a; std::string b; std::string c; }; BOOST_FUSION_ADAPT_STRUCT(Out, a, b, c) int main() { qi::rule<char const*, std::string(), qi::space_type> identifier_rule = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_"); boost::spirit::qi::rule<char const*, Out(), qi::space_type> abc_rule = identifier_rule >> identifier_rule >> identifier_rule; // 测试三种输入场景 std::string tests[] = { "blub blib blab", "\n \n blub \t \t \n blib \t \n blab \n \t \n", " blub blib blab " }; for (auto& test : tests) { Out o; char const* f(test.c_str()); char const* l(f + test.size()); assert(qi::phrase_parse(f, l, abc_rule, qi::space, o)); assert(o.a == "blub"); assert(o.b == "blib"); assert(o.c == "blab"); } return 0; }
补充说明
- 方案2更符合Spirit的设计理念,尤其是解析脚本语言时,自动空白跳过能大幅简化规则定义,避免手动处理各种空白场景。
- 如果后续需要保留某些空白符(比如字符串内部的空格),可以在对应规则中用
qi::lexeme[]包裹,临时禁用跳过器,这是解析复杂语法时常用的技巧。
内容的提问来源于stack exchange,提问作者llm
相关产品推荐
相关产品推荐

