应使用std::unordered_map<std::string, std::string>手动解析Query String,需正确处理%编码和+替换空格,url_decode函数须校验%后两位十六进制合法性,无=项(如flag)和=后空值(如name=)均视为键对应空字符串,避免用std::map除非需有序遍历。

用 std::unordered_map 手动解析最可控
标准库没有直接解析 Query String 的函数,std::unordered_map<std::string, std::string> 是最常用、最轻量的承载结构。手动拆分比引入第三方库更稳妥,尤其在嵌入式或无网络依赖场景下。
关键在于正确处理 % 编码和 + 替代空格——很多手写解析器漏掉这点,导致中文或带空格的值乱码。
- 先用
&拆出多个key=value对 - 每个对再用第一个
=分割(注意 value 可能含=,如filter=a=b&c=d) - 对 key 和 value 分别调用解码函数:将
+换成空格,再将%XX(十六进制)转为对应字节
url_decode 函数必须自己写,别信网上简版
很多示例只处理 %20,但实际可能遇到 %E4%B8%AD(UTF-8 中文),或大小写混用的 %e4%b8%ad。不校验十六进制合法性会导致越界读取或静默失败。
下面是一个健壮的 url_decode 片段:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::string url_decode(const std::string& s) {
std::string ret;
ret.reserve(s.size());
for (size_t i = 0; i < s.size(); ++i) {
if (s[i] == '+') {
ret += ' ';
} else if (s[i] == '%' && i + 2 < s.size()) {
int hex = 0;
if (sscanf(s.c_str() + i + 1, "%2x", &hex) == 1) {
ret += static_cast<char>(hex);
i += 2;
} else {
ret += '%'; // 解码失败,保留原字符
}
} else {
ret += s[i];
}
}
return ret;
}
注意 value 为空时的行为(比如 flag&name=)
Query String 中 flag 这种无 = 的项,应视为 flag="";而 name= 明确表示空字符串。不同后端框架处理不一致,C++ 解析器按 RFC 3986 应统一作空字符串处理。
-
a&b=c→{"a": "", "b": "c"} -
a=&b=→{"a": "", "b": ""} - 不要把
a当作 key 不存在,它存在,只是值为空
避免用 std::map 除非需要有序遍历
std::map 带红黑树开销,插入复杂度 O(log n),而 Query String 通常只有几到几十对,std::unordered_map 平均 O(1) 更合适。如果后续要按字母序输出(比如生成签名),再额外排序,别一开始就用 std::map 损失性能。
另外,如果 value 可能重复(如 tag=a&tag=b),std::unordered_map 会覆盖,这时得改用 std::vector<std::pair<std::string, std::string>> 或 std::multimap ——但这是业务需求决定的,不是解析器该默认支持的。
真正容易被忽略的是编码边界:比如 % 后面不是两个十六进制字符,或者输入含非法 UTF-8 字节,这些情况不加判断就直接 static_cast<char>,会导致数据错位甚至崩溃。

















