编码与字符集
正确处理 URL 中的中文、+ 和 %20
使用工具
同一搜索词却丢了空格,或加号变为空格。URL 问题不仅取决于转了几次,还取决于文字代表哪一部分。完整 URL 中应保留的分隔符,放在值里可能就需要编码。
用 URL 编码工具逐个转换短字符串,只检查表示,不访问 URL;example.invalid 是示例地址。编码看起来正确,不证明资源存在或链接安全。
比较一个空格的两种编码
red blue组件编码得到 red%20blue,表单模式得到 red+blue。它们属于不同约定,并非某个总是错。加号只在应用表单规则的地方代表空格,不是在所有 URL 位置都如此。需确认接收解析规则。
解码也要选择对应规则。普通解码保留 red+blue,把表单加号视为空格才得到 red blue。结果异常时,不要反复解码,先确认输入是普通组件还是表单值。
C++ 的加号不是空格
C++ guideC%2B%2B%20guide
C%2B%2B+guide即使表单模式,原加号也编码为 %2B,以区别由空格生成的 +。直接把未编码的 C++ guide 当表单解码,会把原加号也变空格。必须区分原始与已编码值。
保留还是转换 &,取决于角色
tea & cake组件编码为 tea%20%26%20cake。把原 & 拼进查询可能被当作参数分隔符;反过来把完整 URL 的 ?、& 都当一个值编码,也会破坏结构。需区分构建 URL 与放入单个值。
完整 URL 模式保留部分结构符号,但不会推测意图并修复坏 URL。可用 URL 分解检查主机、路径、查询与片段。查询值中的字面 # 不应意外成为片段起点。
%2520 可能提示多了一层编码
red blue → red%20blue → red%2520blue第二次把百分号编码为 %25,一次解码后仍有 red%20blue,看似没解码。只看值无法判断是误编码两次,还是本来要发送字面 %20,应追踪两端转换。
不要解码到百分号全消失,本来作为文字的 %2F 可能变分隔符并改变含义。正确层数由数据约定决定,不由外观决定。调查时区分编码前、发送前与解析后值。
为什么一个日文字会变成多个 % 序列
あ
UTF-8: e3 81 82
URL: %E3%81%82每个百分号后的两位在此代表一个 UTF-8 字节。あ 占三字节,因而三组百分号;不是把码点 U+3042 写成 %3042。结合文字字节转换,可串起字符、编码字节和 URL 表示三层。
截断的 %E3%81 或非十六进制 %ZZ 无法正确解码。不要猜补字节以消除错误,应检查源值是否截断。旧系统使用其他编码时,本解码器也不能自动确定原字符集。
回到实现时,明确在哪一步编码
记录原搜索词、选择规则、编码值和接收规则。red blue 通过,不代表 C++ guide 或 tea & cake 也正确。保留空格、+、&、#、% 的小例子,可用于 URL 构建变更后的回归。
代码中使用结构化 URL 或查询 API 可减少手工拼接,但 API 已编码值时,不要预先重复编码。实验目的不是增加转换,而是确定由哪一层负责必要转换。
区分值里的 & 与查询分隔符 &
搜索词 red & blue 中的 & 属于值。直接拼接可能被当作新参数边界。组件编码为 red%20%26%20blue,表单编码为 red+%26+blue;两者都用 %26 保护字面 &,只是空格表示不同。保留查询分隔符与保护值内字符是两件事。
排查时并列原值、拼成的 URL、接收值。地址栏可能美化显示,不能仅凭外观认定双重编码。用 URL 分解工具检查参数能否还原 red & blue,无需访问地址。反复解码整个 URL 可能把受保护的 %26 变成结构分隔符 &,每次都应明确转换层。
只有非 ASCII 值失败时,应先查哪层处理 UTF-8 字节,不要立即换字符集。百分号后的两位表示编码字节,不是 Unicode 字符编号。编码变长不等于原字符增加,排查长度限制时应分开测原值与组装 URL。