Query String 是 URL 中 ? 后的键值对序列,需 URL 编码、用 & 连接、= 分隔;直接用 std::map 拼接会漏编码、忽略空值、错用分隔符,导致解析失败或安全漏洞。

什么是Query String,为什么不能直接用std::map拼接
Query String 是 URL 中 ? 后面的键值对序列,形如 name=alice&age=30&city=%E5%8C%97%E4%BA%AC。它要求:键值必须 URL 编码(比如空格变 %20,中文变 UTF-8 + percent-encoding),键值间用 & 连接,键与值之间用 = 连接。直接遍历 std::map 并用 + 拼字符串,会漏掉编码、忽略空值、搞错分隔符,导致后端解析失败或安全漏洞。
手动拼接时必须做URL编码(别信std::hex)
C++ 标准库不提供现成的 URL encode 函数,自己写要处理三件事:识别哪些字符需编码(除字母、数字、-_.~ 外都得转)、按 UTF-8 字节编码、每字节转为 %XX 格式。别用 std::hex 直接格式化字符——它只处理单字节,对 UTF-8 多字节字符会截断。
推荐做法:
- 用
std::string逐字节遍历原始字符串(确保输入已是 UTF-8 编码) - 对每个字节
b,若不在白名单(0-9a-zA-Z-_.~),则写入"%" + two_hex_digits(b) - 别用
std::stringstream << std::hex—— 它默认不补零,0x7变成"7",应是"07";改用std::format("{:02x}", b)(C++20)或sprintf(buf, "%02x", b)
示例片段:
立即学习“C++免费学习笔记(深入)”;
std::string url_encode(const std::string& s) {
std::string out;
for (char c : s) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~') {
out += c;
} else {
out += '%';
out += "0123456789ABCDEF"[((unsigned char)c >> 4) & 0xF];
out += "0123456789ABCDEF"[(unsigned char)c & 0xF];
}
}
return out;
}
遍历std::map<std::string, std::string>拼接时注意空值和顺序
std::map 默认按键升序排序,这在 Query String 中不是必须的,但某些 API 依赖顺序(比如签名计算)。如果 map 里有空值(""),是否保留?多数 HTTP 客户端会发送 key=,但有些后端认为这是“未提供”,最好明确策略。
拼接逻辑要点:
- 跳过 value 为
std::nullopt或空字符串(除非业务要求显式传空) - 先 encode key,再 encode value,中间用
=,项之间用& - 第一项前不加
&,可用 flag 或joiner = ""然后每次循环前加 - 避免在循环内反复
+=小字符串——预估总长并用reserve()提升性能
简写示例:
std::string map_to_query(const std::map<std::string, std::string>& m) {
std::string result;
bool first = true;
for (const auto& [k, v] : m) {
if (!first) result += '&';
result += url_encode(k) + '=' + url_encode(v);
first = false;
}
return result;
}
用第三方库(如cpp-httplib或boost)更省事但有依赖成本
如果项目已用 boost,boost::algorithm::hex 和 boost::url(v1.79+)能简化编码;cpp-httplib 虽无内置 query builder,但其 Client::Get() 内部实现可参考。不过引入依赖只为拼个 query string 往往不划算——10 行手写函数比配 CMake + 链接 boost 更快、更可控。
真正要注意的是边界情况:键含 = 或 &(虽少见但合法)、value 为 null(C++ 里对应 std::optional<std::string>)、map 为空时返回空字符串而非 "?"——后者是 URL 路径的一部分,不是 query string 本身。


















