必须被百分比转义的字符是除a-z、a-z、0-9、-、.、_、~之外的所有字符;空格须编码为%20而非+;utf-8字符串应按字节处理,不需解码为unicode码点;手写encode函数需遍历unsigned char、查unreserved白名单、用大写两位十六进制格式化。

哪些字符必须被百分比转义?
URL编码不是“把所有非字母数字都转”,而是严格按 rfc3986 定义的 unreserved 字符集来判断:只有 A-Z、a-z、0-9、-、.、_、~ 这些可以不编码;其余如空格、/、?、#、[、]、中文、emoji 等,一律需转为 %XX 形式(大写十六进制)。
特别注意:+ 不是安全字符——它在 query string 中常被误用作空格替代,但 RFC 明确要求空格应编码为 %20,而非 +;后者只是 application/x-www-form-urlencoded 的历史变体,和标准 URL 编码无关。
如何手写一个安全可靠的 encode 函数?
别依赖 std::hex 直接格式化——它默认小写且不补零,也不处理宽字符。核心逻辑就三步:
- 遍历每个
char或unsigned char(避免符号扩展) - 查表或用
isalnum+ 手动比对-._~ - 对需编码字节,用
std::stringstream或fmt::format生成%XX,确保两位、大写、无空格
示例关键片段:
std::string url_encode(const std::string& s) {
std::string result;
result.reserve(s.size() * 3);
for (unsigned char c : s) {
if ((c >= 'A' && c = 'a' && c = '0' && c <h3>UTF-8 字符串怎么处理?</h3><p>C++ std::string 存的是字节,不是字符。RFC 要求对 UTF-8 编码后的每个字节做百分比转义,而不是对 Unicode 码点。这意味着你不需要先 decode 成 <code>char32_t</code>,直接按字节处理即可——只要输入是合法 UTF-8(比如来自 <code>std::u8string</code> 或明确标注的 UTF-8 字符串)。</p><p>常见陷阱:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 把
std::wstring直接丢给 encode 函数——Windows 上通常是 UTF-16,会把 surrogate pair 当成两个乱码字节编码 - 用
std::codecvt_utf8<wchar_t></wchar_t>转换,但该类已在 C++17 中被弃用,且跨平台行为不一致 - 未验证输入是否为合法 UTF-8,导致多字节序列被错误拆解(例如把
0xC3 0xB6(ö)当成两个独立字节分别编码)
稳妥做法:只接受 std::string 输入,并文档注明“输入应为 UTF-8 编码”。必要时可加轻量级 UTF-8 验证(检查首字节范围、后续字节是否为 0x80–0xBF)。
为什么不能用 curl_easy_escape 或其他第三方函数?
curl_easy_escape 默认行为不符合 RFC:它把空格编码为 +,且不支持 ~ 等 unreserved 字符白名单(它用的是旧式“除了字母数字和几个符号外全转”的宽松策略)。更严重的是,它内部使用 malloc 分配内存,返回 char*,调用者必须记得 curl_free,容易内存泄漏。
其他常见坑:
-
boost::network::uri::encode依赖 Boost.Network,而该库已停止维护 - 某些“轻量 URL encode” snippet 忘了处理
0x00(空字节),导致std::string截断 - 用
std::to_string((int)c)再转十六进制——性能差,且没补零
真正需要兼容性或复杂场景(比如带 fragment 的完整 URI 构建),建议用 cpp-httplib 或 crow 的 URI 模块,它们内部实现严格遵循 rfc3986。
最简、最可控的方式,还是自己写那个 20 行以内的 url_encode 函数——边界清晰,无隐式依赖,改起来也快。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










