判断URL路径字符串合法性应聚焦path段约束:允许字母、数字、/、-、_、.、~、!、$、&、'、(、)、*、+、,、;、=及合法%XX编码,禁用空格、控制字符和未编码特殊符号,且需单独检测../等路径穿越序列。

URL路径字符串的合法性 ≠ 完整URL校验
直接用 std::regex 匹配完整 URL 规则(比如 RFC 3986)既难写全又容易误判——你真正要判断的,通常是「这段字符串能否作为 URL 的 path 部分安全使用」,比如拼接进 https://example.com/xxx 后是否合法。标准库不提供现成的 URL 解析器,所以得聚焦在 path 段的约束上:不能含空格、不能有未编码的控制字符、不能以 .. 开头导致路径穿越(除非你明确允许)、不能含非法字节序列(如 UTF-8 无效序列)。
用 std::regex 快速过滤明显非法 path 字符
对大多数内部服务或前端路由场景,先排除明显危险字符更实用。RFC 3986 允许 path 中出现的字符有限,但实际中常见需求是:接受字母、数字、/、-、_、.、~、!、$、&、'、(、)、*、+、,、;、=,以及已百分号编码的字节(如 %20)。其他一律视为非法。
可这样写正则:
std::regex path_regex(R"(^([A-Za-z0-9\-._~!$&'()*+,;=]|%[0-9A-Fa-f]{2}|/)*$)");
注意点:
立即学习“C++免费学习笔记(深入)”;
-
%[0-9A-Fa-f]{2}只匹配合法的百分号编码,但不校验其解码后是否为有效 UTF-8 —— 若需严格验证,得额外用std::codecvt_utf8或第三方库(如 ICU)解码再检查 - 该正则不禁止
../或/../,如果用于文件系统路径拼接,必须单独扫描..和.序列 - 空字符串
""是合法 path(对应根路径),但"/"也是,两者语义不同,需按业务区分
手动遍历校验比正则更可控
正则在嵌入式或低版本 STL 环境下可能不可用,且难以调试。改用逐字节检查更可靠:
核心逻辑是:对每个字符 c,判断它是否属于「允许未编码字符集」,或是否为合法的 %XX 编码开头。
- 遇到
%时,必须后面紧跟两个十六进制字符,否则非法 - 遇到 ASCII 控制字符(
0x00–0x1F、0x7F)直接拒绝 - 遇到非 ASCII 字节(高位为 1)时,应视为 UTF-8 起始字节,需验证其后续字节是否构成合法 UTF-8 序列;若业务只处理 ASCII path,可直接拒绝所有
>= 0x80字节 - 路径穿越检测建议独立做:用
std::string_view扫描".."和".",特别注意边界(如"a..b"不算穿越,但"../"或"./"是)
别忽略平台和协议差异
同一个 path 字符串,在不同上下文里合法性不同:
- HTTP URL 的 path 允许
/开头,但 Windows 文件路径不允许(除非是 UNC 路径) - WebSocket 的
ws://path 和 HTTP 基本一致,但某些代理会额外限制长度或字符 - 如果你最终要把这个 path 传给
std::filesystem::path构造,那还要考虑操作系统路径规则(如 Windows 不允许< > : " | ? *)——这和 URL path 规范是两套体系,不能混用 - 浏览器端 JavaScript 的
URL构造函数会自动 normalize path(如合并//、解析./),而 C++ 没这层,得自己决定是否要做 normalization
真正麻烦的不是「怎么写正则」,而是搞清你要防什么:是防 XSS?防路径穿越?还是仅避免 HTTP 请求被网关拒绝?目标不同,校验粒度就完全不同。


















