RFC 3986明确规定URL必须转义除A-Z、a-z、0-9、-、.、_、~外的所有字符,包括空格(%20)、中文(UTF-8字节逐字编码)、/(%2F)、?、&、=等保留字符及控制字符(0x00–0x20、0x7F–0xFF)。

URL编码必须转义哪些字符
RFC 3986 明确规定,URL 中只允许出现 unreserved 字符(A-Z、a-z、0-9、-、.、_、~),其余所有字符——包括空格、中文、标点、控制字符——都必须被百分号编码。特别注意:/、?、:、@、&、=、+、$、,、; 等在 URL 中有语法意义的字符,**即使出现在 path 或 query value 中也必须编码**,不能“按需保留”。
- 空格必须编码为
%20(不是+;后者是 application/x-www-form-urlencoded 的行为,不适用于通用 URL 编码) - 中文如
你好→%E4%BD%A0%E5%A5%BD(UTF-8 字节序列逐字节编码) -
/在 path segment 中若非分隔符用途(例如文件名含斜杠),也得编码为%2F
用 std::ostringstream + std::hex 实现安全编码
标准库没有内置 URL encode 函数,但可用 std::ostringstream 配合 std::hex 和 std::setw(2) 构建可读、无依赖的实现。关键在于:先将输入字符串按 UTF-8 解释为字节序列(std::string 本身即字节数组),再对每个字节判断是否属于 unreserved。
std::string url_encode(const std::string& s) {
std::ostringstream encoded;
encoded.fill('0');
encoded << std::hex;
for (unsigned char c : s) {
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '.' || c == '_' || c == '~') {
encoded << c;
} else {
encoded << '%' << std::setw(2) << static_cast<int>(c);
}
}
return encoded.str();
}
- 务必用
unsigned char遍历,避免char为负值导致static_cast<int>出现符号扩展错误(如0xFF变成-1) -
std::setw(2)必须每次写入前重置,std::ostringstream不自动保持宽度 - 该函数假设输入已是合法 UTF-8;若传入 GBK 或乱码,编码结果仍符合 RFC,但语义错误——URL 编码不负责字符集转换
别踩 std::to_string((int)c) 的坑
常见错误是用 std::to_string((int)c) 拼接十六进制,比如 "%" + std::to_string((int)c)。这会输出十进制数字(255),而非两位十六进制(FF),完全不符合规范。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
std::to_string永远输出十进制,和 URL 编码无关 - 不要手写查表或位运算模拟 hex 转换——易出错且无必要
- 若不用
std::ostringstream,可用sprintf(buf, "%%02X", c),但要注意缓冲区大小和大小写(RFC 接受大写,但惯例用大写)
解码时要严格校验 %XX 格式
URL 解码比编码更易出错。不能简单替换所有 %xx,必须验证:以 % 开头、后跟两个十六进制字符、且不能跨字节截断 UTF-8 序列(不过解码本身只操作字节,UTF-8 合法性应由上层保证)。
立即学习“C++免费学习笔记(深入)”;
- 遇到
%g7、%zz、%结尾等非法格式,应拒绝解码或保留原样(取决于策略) -
%20解码为0x20字节,不是' '字符——解码结果仍是std::string字节流,后续是否 reinterpret_cast 为 UTF-8 字符串由调用方决定 - 某些场景(如 CGI)要求解码后再次校验路径遍历(
%2E%2E→..),这已超出纯编码/解码范畴
真正麻烦的是多字节 UTF-8 字符的边界处理——编码器不关心,但解码器若把 %E4%BD%A0 错拆成 E4、BD、A0 三个独立字节就全乱了。所以解码必须严格按 % + 两位 hex 成组消费,不能跳过或粘连。

















