必须编码的字符是除a-z、a-z、0-9、-、.、_、~外的所有字节;rfc3986中非保留字符仅限这7类,其余如:/?#[]@!$&等保留字符若作数据使用也须编码,中文emoji等utf-8多字节字符需逐字节编码。

哪些字符必须被编码?RFC3986的保留字符和非保留字符怎么区分
URL百分比编码不是“把所有非字母数字都编码”,而是严格按 RFC3986 划分:保留字符(:、/、?、#、[、]、@、!、$、&、、<code>+、,、;、=)在特定上下文中具有语法意义,不能随意编码;而未保留字符(字母、数字、-、_、.、~)**绝不能编码**——哪怕出现在 query value 里也得原样保留。
常见错误是把 _ 或 . 编成 %5F、%2E,这违反 RFC3986,会导致某些服务端(如 AWS S3 presigned URL、GitHub API)校验失败。
-
std::string中需逐字节判断:ASCII 值在0x20–0x7E范围内,且不属于未保留字符集合的,才需编码 - 空格必须编码为
%20(不是+,那是 application/x-www-form-urlencoded 的规则) - 非 ASCII 字符(如中文)必须先 UTF-8 编码,再对每个字节做百分比编码 —— C++11
std::string本身不存 Unicode,你得自己确保输入是 UTF-8 字节序列
用 std::ostringstream + std::hex 实现安全编码,避开 std::stringstream 的十六进制陷阱
std::stringstream 默认用 std::hex 会丢前导零,比如 0x0A 变成 a 而不是 0a,导致 %0A 变成 %a —— 这是非法 URL 编码。必须手动补零或换方式。
更稳妥的做法是用 std::ostringstream 配合 std::setfill('0') 和 std::setw(2):
#include <sstream>
#include <iomanip>
#include <string>
std::string url_encode(const std::string& s) {
std::ostringstream encoded;
for (unsigned char c : s) {
if ((c >= 'a' && c = 'A' && c = '0' && c
<p>注意:<code>std::hex</code> 和 <code>std::uppercase</code> 是流状态,需在每次写入前重置(或用临时 ostringstream 分离作用域),否则可能影响后续调用。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)"><img
src="https://img.php.cn/upload/manual/000/000/001/5d6de31fedca2993.png" alt="C函数速查手册(CHM版)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="overflowclass">C函数速查手册(CHM版)</a>
<p class="overflowclass">C函数速查手册(CHM版)</p>
</div>
<a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<h3>如何处理 UTF-8 多字节字符?别直接遍历 char</h3>
<p>如果输入字符串含中文、emoji 等,它本质是 UTF-8 字节流。<code>for (char c : s)</code> 会把一个汉字拆成 2–4 个独立字节分别编码,结果正确(因为 RFC3986 要求对原始字节编码),但前提是:你传进来的 <code>std::string</code> 确实是合法 UTF-8。</p>
<p>没做验证就直接编码,遇到非法 UTF-8(如截断的 <code>\xE4\xB8</code>)会导致部分字节被单独编码,生成的 URL 可能被服务端拒绝或解码出错。</p>
<ul>
<li>生产环境建议先用轻量级 UTF-8 校验(例如检查每个字节是否符合 UTF-8 编码规则)</li>
<li>不要用 <code>std::wstring</code> 或 <code>std::codecvt_utf8</code> —— 后者在 C++17 被弃用,且跨平台行为不一致</li>
<li>若需从 <code>std::wstring</code> 转 UTF-8,用 <code>std::wstring_convert<:codecvt_utf8>></:codecvt_utf8></code>(仅限 C++11/14)或第三方库(如 utf8cpp)</li>
</ul>
<h3>std::string_view 参数能避免拷贝,但要注意生命周期</h3>
<p>如果你频繁编码短字符串(如 HTTP header value),用 <code>std::string_view</code> 当参数比 <code>const std::string&</code> 更高效,避免隐式构造临时 <code>std::string</code>:</p>
<pre class="brush:php;toolbar:false;">
std::string url_encode(std::string_view s) { ... }
但必须确保调用方传入的 view 所指内存在函数返回后仍有效。比如这样写就危险:
auto encoded = url_encode(std::string{"hello 世界"}.substr(0, 5)); // substr 返回临时 string,view 悬空
容易被忽略的是:C++20 之前没有 std::string_view::data() 的 const 正确性保证,某些旧编译器(如 GCC 7)对 string_view 的 lifetime 推导不严谨,建议在关键路径加 static_assert 或单元测试覆盖边界场景。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










