🔗 URL 编码完全指南:百分号编码原理、encodeURI vs encodeURIComponent 详解
2026-07-06 · 约 10 分钟
你有没有在浏览器地址栏里见过这样的东西:https://example.com/search?q=%E4%B8%AD%E5%9B%BD?那个 %E4%B8%AD%E5%9B%BD 就是「中国」两个字的 URL 编码形式。
URL 编码(也叫「百分号编码」/ Percent Encoding)是 Web 开发中最基础也最容易踩坑的知识点之一。这篇文章一次给你讲透。
01 为什么需要 URL 编码?
URL 的设计初衷只支持 ASCII 字符集,而且其中很多字符有特殊含义。比如 / 用来分隔路径、? 表示查询参数的开始、# 表示锚点。
如果你想把「中国」或一个空格塞进 URL 里,直接放进去会出问题。URL 编码解决了两个核心问题:
- 非 ASCII 字符(中文、日文、特殊符号)→ 转换成浏览器能理解的形式
- 特殊字符(
?、&、#等)→ 转义后不再被解析为语法符号
02 编码规则:百分号 + 两位十六进制
规则非常简单:把一个字符的 UTF-8 字节值前面加上 %。
举个例子,「中」字的 UTF-8 编码是 E4 B8 AD(三个字节),所以 URL 编码后就是 %E4%B8%AD。
字符 UTF-8 字节 URL 编码
------------------------------------------
中 E4 B8 AD %E4%B8%AD
国 E5 9B BD %E5%9B%BD
空格 20 %20
! 21 %21
~ 7E %7E
💡 注意空格可以编码为 %20,但在 application/x-www-form-urlencoded 里也可以编码为 +。不过 URL 路径里 + 就是字面加号,不会变成空格——这是很多人踩的坑。
03 保留字符 vs 非保留字符
RFC 3986 把 URL 里的字符分成两类:
非保留字符(不需要编码):
A-Z a-z 0-9 - _ . ~
保留字符(有特殊含义,需要编码):
: / ? # [ ] @ ! $ & ' ( ) * + , ; =
——但这有个微妙的地方:保留字符「在特殊含义的语境下才需要编码」。比如 / 在路径分隔符的位置不需要编码,但如果你要把 / 作为查询参数的值,那就得编码成 %2F。
04 encodeURI vs encodeURIComponent:最常踩的坑
JavaScript 提供了两个函数,它们的区别是 面试必考题、开发必踩坑:
对整个 URL 编码,保留 URL 语义
encodeURI("https://ex.com/路径?q=你好")
// 结果:
// https://ex.com/%E8%B7%AF%E5%BE%84?q=%E4%BD%A0%E5%A5%BD
// ❌ 不会编码这些:
// : / ? # [ ] @ ! $ & ' ( ) * + , ; =
// 所以 query 参数值里如果有
// & 或 =,会被当成 URL 语法解析
编码查询参数值,编码所有非字母数字
encodeURIComponent("你好&世界")
// 结果:%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C
// ✅ & 被编码为 %26
// ✅ = 被编码为 %3D
// ✅ / 被编码为 %2F
// 适合用于 query 参数值
黄金法则:
- 对整个 URL 略作处理 →
encodeURI - 对 query 参数值编码 →
encodeURIComponent
⚠️ 很多新手在拼接 URL 时用 encodeURI("https://api.com?name=" + userInput),当 userInput 包含 & 时,参数就断掉了。正确做法是对参数值用 encodeURIComponent 单独编码。
05 各种语言的 URL 编码实现
JavaScript
// 编码整个 URL(保留 URL 结构)
encodeURI("https://example.com/路径")
// → "https://example.com/%E8%B7%AF%E5%BE%84"
// 编码参数值(安全!)
encodeURIComponent("你好&世界=test")
// → "%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C%3Dtest"
// 解码
decodeURI("%E8%B7%AF%E5%BE%84")
// → "路径"
decodeURIComponent("%E4%BD%A0%E5%A5%BD%26")
// → "你好&"
Python
from urllib.parse import quote, unquote, urlencode
# 编码
quote("你好") # → '%E4%BD%A0%E5%A5%BD'
quote("/", safe="") # → '%2F' (safe='' 表示不保留任何字符)
# 解码
unquote('%E4%BD%A0%E5%A5%BD') # → '你好'
# 构建 query string
urlencode({"q": "你好&世界", "page": 1})
# → 'q=%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C&page=1'
Java
import java.net.URLEncoder;
import java.net.URLDecoder;
// 编码(注意:会把空格变成 +)
URLEncoder.encode("你好", "UTF-8")
// → "%E4%BD%A0%E5%A5%BD"
// 解码
URLDecoder.decode("%E4%BD%A0%E5%A5%BD", "UTF-8")
// → "你好"
💡 Java 的 URLEncoder 会把空格编码为 +(遵循 application/x-www-form-urlencoded 规范),而不是 %20。如果你的后端是 Java/Spring,解码时注意处理 +。
PHP
<?php
// 编码
urlencode("你好&世界")
// → "%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C"
rawurlencode("你好&世界")
// → "%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C"
// 区别:urlencode 把空格变成 +,rawurlencode 把空格变成 %20
// 推荐使用 rawurlencode,兼容 RFC 3986
// 解码
urldecode("%E4%BD%A0%E5%A5%BD")
urldecode("%E4%BD%A0%E5%A5%BD")
Go
import "net/url"
// 编码
url.QueryEscape("你好&世界")
// → "%E4%BD%A0%E5%A5%BD%26%E4%B8%96%E7%95%8C"
url.PathEscape("你好")
// → "%E4%BD%A0%E5%A5%BD"
// 解码
url.QueryUnescape("%E4%BD%A0%E5%A5%BD")
// → "你好", nil
06 URL 编码的 5 个常见坑
坑 1:用 encodeURI 拼接参数值
// ❌ 错误
const url = "https://api.com?q=" + encodeURI(userInput)
// 如果 userInput = "hello&world",url 中 q=hello,world 被当成了新参数
// ✅ 正确
const url = "https://api.com?q=" + encodeURIComponent(userInput)
坑 2:对已经编码的字符串再次编码
// 如果 input 已经是 "%E4%BD%A0%E5%A5%BD"
encodeURIComponent(input)
// → "%254%E4%25BD%25A0%E5..." (% 被转成了 %25)
// ✅ 再次解码后再编码,或者保持原样
decodeURIComponent(input)
坑 3:Java 的 + 号迷思
Java 的 URLEncoder 把空格变成 +,但 JavaScript 的 decodeURIComponent 不认 +。如果你前端 JS 解码 Java 编码的数据,记得先替换 + 为 %20。
坑 4:忘了编码 # 和 /
# 在 URL 里是片段标识符(锚点),/ 是路径分隔符。如果你把文件路径或 ID 作为参数传递,一定要用 encodeURIComponent 把它们转掉。
坑 5:不同规范的空格编码
application/x-www-form-urlencoded: 空格 → +
RFC 3986 URL 路径: 空格 → %20
HTML form GET 提交: 空格 → +
JSON 中的 URI: 空格 → %20
07 总结速查表
| 场景 | 用什么 | 空格编码 |
|---|---|---|
| 拼接 URL 路径段 | encodeURIComponent / rawurlencode | %20 |
| 拼接 query 参数值 | encodeURIComponent / url.QueryEscape | %20 或 + |
| 表单提交 (POST form) | encodeURIComponent | + |
| 对整个 URL 轻微编码 | encodeURI | %20 |
JavaScript URL 对象 | 浏览器自动处理 | 自动 |
| Python requests 库 | params 参数自动处理 | 自动 |
08 延伸阅读
- UUID/GUID 完全指南 — 另一个常见的标识符知识点
- Base64 编码指南 — 另一种常见的编码方式
- RFC 3986 — URI 的官方规范