百分号编码的用途

URL 是一种小而严格的语法。少数字符具有结构含义:/ 分隔路径段,? 开始查询,& 分隔参数,# 标记片段,:@ 在授权部分中担任角色。那么当这些字符之一需要作为普通数据出现时会发生什么,比如一个字面包含 & 的搜索词,或一个带空格的路径段?你不能原样放入,因为 URL 解析器会把它读作结构并出错。百分号编码,在 RFC 3986 中定义,常称为 URL 编码,是解决这个问题的转义机制:它把一个不安全的字符重写为一个 %,后跟其字节值的两个十六进制数字。

一个空格变成 %20,一个斜杠变成 %2F,一个 & 符号变成 %26。文本 a b/c 被编码为 a%20b%2Fc。解码器将其逆转:每个 %XX 重新变回字节 XX,原始字符就回来了。

非保留集:保持不变的部分

百分号编码并不触碰一切。RFC 3986 定义了一个小的非保留字符集,在 URL 中总是可以安全地按字面保留,而且永远不应被转义:

  • 字母 A-Za-z
  • 数字 0-9
  • 四个标记 -._~

其余的一切,包括保留的结构字符和 ASCII 之外的任何东西,当作为数据出现时都会被百分号编码。127 以上的字节的处理方式是先把文本编码为 UTF-8,然后对每个结果字节进行百分号编码,这就是为什么单个带重音的字母或一个表情符号会变成一系列多个 %XX 对,每个 UTF-8 字节一个。

为什么 %XX 是两个十六进制数字

% 是一个转义标记;它后面的两个字符是十六进制形式的字节,正是一个字节的十六进制编码。这就是为什么一个有效的转义始终是 % 加两个十六进制数字、别无其他的全部原因。一个后面跟着不是两个十六进制数字的东西的 %,比如 %2G,或字符串末尾一个孤零零的 %,是格式错误的,而一个细心的解码器会报告它而不是猜测。

它与 Base64 有何不同

人们很容易把百分号编码与 Base64 混为一谈,但它们回答的是不同的问题。Base64 取任意二进制并把它整体变得对文本通道安全,把每个输入膨胀约三分之一。百分号编码让文本中已经安全的大部分保持不动,只转义会造成麻烦的少数字符。对于主要是字母和数字的普通文本,百分号编码紧凑得多,并且对人保持可读;对于原始二进制,几乎每个字节都需要转义,它效率低得可怕,而 Base64 或其 URL 安全变体才是合适的工具。

简而言之:百分号编码是对进入 URL 的文本的选择性转义;Base64 是对去往任何只接受文本之处的字节的完整重新编码。

试一试

codec 工具中选择 Percent,对文本进行百分号编码或把一个 %XX 字符串解码回来,全部在你的浏览器中完成。它把非 ASCII 内容编码为 UTF-8 字节,标记格式错误的转义,并告诉你解码结果何时是二进制而非可读文本。