使用Boost.Qi解析逗号分隔字符/字符串序列至std::map的代码修正与功能扩展求助
修复并扩展Boost Spirit解析器:从字符到字符串序列的映射
作为Boost Spirit的初学者,你的代码里有几个明显的语法和逻辑问题,我来一步步帮你修复,再扩展到字符串序列的解析需求。
原代码的核心问题
- 语法规则的括号不匹配:
char_[v.insert(std::make_pair(_1,0)]和后续的插入语句都缺少闭合括号,导致编译失败 - 序号逻辑错误:所有插入的value都是0,没有实现从1开始递增的序号
- 全局变量
i未被正确使用,而且全局变量会导致多次解析时序号累加(而非每次重置为1) - main函数中迭代器类型不匹配:你用
std::map<std::string, int>::iterator去遍历std::map<char,int>,类型完全不兼容 - 元素访问错误:对于
std::map<char,int>,it->first是char类型,不需要强制转成std::string
修复后的字符序列解析(std::map<char,int>)
下面是修复后的完整代码,我会标注关键修改点:
#include <iostream> #include <string> #include <map> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace myparser { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace phoenix = boost::phoenix; template <typename Iterator> bool parse_chars(Iterator first, Iterator last, std::map<char, int>& result) { // 每次解析重置计数器,避免全局变量的问题 int counter = 0; bool r = qi::phrase_parse( first, last, // 语法规则:单个char,后面跟任意数量的", char" (qi::char_[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))] >> *(',' >> qi::char_[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))])) , ascii::space // 跳过空格 ); // 确保整个输入都被解析,没有残留字符 return r && (first == last); } } // namespace myparser int main() { std::string str; while (std::getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; std::map<char, int> char_map; if (myparser::parse_chars(str.begin(), str.end(), char_map)) { std::cout << "-------------------------\n"; std::cout << "Parsing succeeded\n"; std::cout << str << " Parses OK:\n"; // 使用正确的迭代器类型遍历map for (const auto& pair : char_map) { std::cout << pair.first << " :: " << pair.second << "\n"; } std::cout << "-------------------------\n"; } else { std::cout << "-------------------------\n"; std::cout << "Parsing failed\n"; std::cout << "-------------------------\n"; } } return 0; }
关键修改说明
- 去掉无用全局变量:把计数器
counter定义在解析函数内部,每次解析都会重置为0,避免多次调用的副作用 - 正确的序号递增:用
++phoenix::ref(counter)实现每次匹配字符时序号自增,确保第一个字符是1,第二个是2,以此类推 - 修正语法括号:补全所有缺失的闭合括号,让Spirit语法规则合法
- 简化迭代遍历:用C++11的范围for循环替代手动迭代,避免类型错误
- 严格匹配检查:返回
r && (first == last),确保整个输入字符串都被解析,没有残留的无效字符
扩展:解析字符串序列到std::mapstd::string,int
要解析逗号分隔的字符串序列,只需要修改Spirit的语法规则,把qi::char_替换成匹配字符串的规则即可。下面是扩展后的完整代码:
#include <iostream> #include <string> #include <map> #include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> namespace myparser { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace phoenix = boost::phoenix; // 解析字符序列到std::map<char,int> template <typename Iterator> bool parse_chars(Iterator first, Iterator last, std::map<char, int>& result) { int counter = 0; bool r = qi::phrase_parse( first, last, (qi::char_[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))] >> *(',' >> qi::char_[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))])) , ascii::space ); return r && (first == last); } // 新增:解析字符串序列到std::map<std::string,int> template <typename Iterator> bool parse_strings(Iterator first, Iterator last, std::map<std::string, int>& result) { int counter = 0; // 定义字符串规则:匹配所有非逗号的字符(忽略空格的话用lexeme) auto string_rule = qi::lexeme[+(ascii::char_ - ',')]; bool r = qi::phrase_parse( first, last, (string_rule[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))] >> *(',' >> string_rule[phoenix::insert(result, phoenix::make_pair(_1, ++phoenix::ref(counter)))])) , ascii::space ); return r && (first == last); } } // namespace myparser int main() { std::string str; std::cout << "输入字符序列(比如a,b,c)或字符串序列(比如apple,banana,cherry),输入q退出:\n"; while (std::getline(std::cin, str)) { if (str.empty() || str[0] == 'q' || str[0] == 'Q') break; // 先尝试解析字符序列,失败则尝试解析字符串序列 std::map<char, int> char_map; if (myparser::parse_chars(str.begin(), str.end(), char_map)) { std::cout << "-------------------------\n"; std::cout << "字符解析成功\n"; for (const auto& pair : char_map) { std::cout << pair.first << " :: " << pair.second << "\n"; } std::cout << "-------------------------\n"; } else { std::map<std::string, int> str_map; if (myparser::parse_strings(str.begin(), str.end(), str_map)) { std::cout << "-------------------------\n"; std::cout << "字符串解析成功\n"; for (const auto& pair : str_map) { std::cout << pair.first << " :: " << pair.second << "\n"; } std::cout << "-------------------------\n"; } else { std::cout << "-------------------------\n"; std::cout << "解析失败,请检查输入格式\n"; std::cout << "-------------------------\n"; } } } return 0; }
扩展部分说明
- 字符串规则定义:
qi::lexeme[+(ascii::char_ - ',')]表示匹配一个或多个非逗号的字符,lexeme的作用是让Spirit忽略字符串内部的空格(如果不需要忽略空格,可以去掉lexeme) - 复用计数器逻辑:和字符解析一样,每次解析重置计数器,确保序号从1开始递增
- 双解析分支:main函数里先尝试字符解析,失败则尝试字符串解析,兼容两种输入格式
内容的提问来源于stack exchange,提问作者Souhaielrmx
相关产品推荐
相关产品推荐

