URL 编解码:encodeURIComponent
URL 含中文、空格、特殊字符,浏览器请求报错;URL 参数含 & 被截断——URL 编解码解决这些。本文详解 URL 编码。
什么是 URL 编码?
URL 编码(百分号编码)把特殊字符转为 %XX 形式:
hello world → hello%20world
中文 → %E4%B8%AD%E6%96%87
& → %26
= → %3D
为什么需要 URL 编码?
1. URL 只允许 ASCII
URL 标准只允许 ASCII 字符(A-Z a-z 0-9 和部分符号)。其他字符必须编码。
2. 特殊字符有语义
URL 中 & 分隔参数、= 分隔键值、/ 分隔路径、? 开始查询。这些字符在数据中必须编码避免歧义。
3. 中文等非 ASCII 字符
中文字符用 UTF-8 编码后百分号编码:
中文 → UTF-8: E4 B8 AD E6 96 87 → %E4%B8%AD%E6%96%87
JavaScript 中的 URL 编码
encodeURI
编码整个 URL,保留 URL 结构字符:
encodeURI('https://example.com/path with space/?q=中文')
// 'https://example.com/path%20with%20space/?q=%E4%B8%AD%E6%96%87'
保留:;,/?:@&=+$#。
encodeURIComponent
编码 URL 参数,编码所有特殊字符:
encodeURIComponent('path with space/?q=中文')
// 'path%20with%20space%2F%3Fq%3D%E4%B8%AD%E6%96%87'
只保留:A-Z a-z 0-9 - _ . ! ~ * ' ( )
区别
| 字符 | encodeURI | encodeURIComponent |
| 空格 | %20 | %20 |
| 中文 | %E4%B8%AD | %E4%B8%AD |
| / | / | %2F |
| ? | ? | %3F |
| & | & | %26 |
| = | = | %3D |
| # | # | %23 |
| + | + | %2B |
何时用哪个?
- 整个 URL →
encodeURI - URL 参数值 →
encodeURIComponent
decodeURI / decodeURIComponent
解码:
decodeURI('https://example.com/path%20with%20space/')
// 'https://example.com/path with space/'
decodeURIComponent('path%20with%20space')
// 'path with space'
52tool URL 编解码工具
工具地址:URL 编解码
功能
- URL 编码(encodeURI 风格)
- URL 参数编码(encodeURIComponent 风格)
- URL 解码
- 浏览器端处理
实战 1:编码 URL
输入:
https://example.com/search?q=hello world&lang=zh
输出(encodeURI):
https://example.com/search?q=hello%20world&lang=zh
输出(encodeURIComponent):
https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dhello%20world%26lang%3Dzh
实战 2:编码参数
输入:
中文测试 & special=chars
输出:
%E4%B8%AD%E6%96%87%E6%B5%8B%E8%AF%95%20%26%20special%3Dchars
实战 3:解码
输入:
https%3A%2F%2Fexample.com%2F%3Fq%3D%E4%B8%AD%E6%96%87
输出:
https://example.com/?q=中文
哪些字符需要编码?
保留字符(有语义)
; , / ? : @ & = + $ #
这些在 URL 不同部分语义不同,数据中需编码。
不安全字符
空格 " < > { } | \ ^ [ ] `
可能被网关、代理转义,建议编码。
非 ASCII 字符
中文、日文、韩文、emoji 等
UTF-8 编码后百分号编码。
不需编码
A-Z a-z 0-9 - _ . ~
常见编码表
| 字符 | 编码 | |
| 空格 | %20(或 +) | |
| ! | %21 | |
| " | %22 | |
| # | %23 | |
| $ | %24 | |
| % | %25 | |
| & | %26 | |
| ' | %27 | |
| ( | %28 | |
| ) | %29 | |
| * | %2A | |
| + | %2B | |
| , | %2C | |
| / | %2F | |
| : | %3A | |
| ; | %3B | |
| < | %3C | |
| = | %3D | |
| > | %3E | |
| ? | %3F | |
| @ | %40 | |
| [ | %5B | |
| \ | %5C | |
| ] | %5D | |
| ^ | %5E | |
| ` | %60 | |
| { | %7B | |
| \ | %7C | |
| } | %7D | |
| ~ | ~ |
空格的两种编码
%20:URL 标准+:表单提交(application/x-www-form-urlencoded)
JavaScript encodeURIComponent(' ') → %20。
PHP urlencode(' ') → +。
编程实现
JavaScript
// 编码
encodeURI(url);
encodeURIComponent(param);
// 解码
decodeURI(encoded);
decodeURIComponent(encoded);
Python
from urllib.parse import quote, unquote, urlencode
# 编码
quote('hello world') # 'hello%20world'
quote('https://example.com/?q=test', safe=':/?=&') # 保留 URL 字符
# 参数编码
urlencode({'q': '中文', 'page': 1})
# 'q=%E4%B8%AD%E6%96%87&page=1'
# 解码
unquote('hello%20world') # 'hello world'
PHP
// 编码
$encoded = urlencode('hello world & 中文'); // 'hello+world+%26+%E4%B8%AD%E6%96%87'
$encoded = rawurlencode('hello world & 中文'); // 'hello%20world%20%26%20%E4%B8%AD%E6%96%87'
// 解码
$decoded = urldecode('hello%20world');
$decoded = rawurldecode('hello%20world');
Java
import java.net.URLEncoder;
import java.net.URLDecoder;
String encoded = URLEncoder.encode("中文", "UTF-8");
String decoded = URLDecoder.decode("%E4%B8%AD%E6%96%87", "UTF-8");
Go
import "net/url"
encoded := url.QueryEscape("中文")
decoded, _ := url.QueryUnescape("%E4%B8%AD%E6%96%87")
实战场景
场景 1:URL 参数含中文
错误:https://example.com/search?q=中文
正确:https://example.com/search?q=%E4%B8%AD%E6%96%87
JavaScript:
const url = `https://example.com/search?q=${encodeURIComponent('中文')}`;
场景 2:参数含特殊字符
参数值:a=1&b=2
未编码:?data=a=1&b=2 ← b=2 被当作新参数
编码后:?data=a%3D1%26b%3D2
场景 3:构造查询字符串
const params = {
q: 'hello world',
page: 1,
tag: '中文'
};
const query = new URLSearchParams(params).toString();
// 'q=hello+world&page=1&tag=%E4%B8%AD%E6%96%87'
场景 4:路径含中文
https://example.com/files/报告.pdf
→ https://example.com/files/%E6%8A%A5%E5%91%8A.pdf
场景 5:JSON 数据传 URL
const data = { name: '张三', age: 30 };
const encoded = encodeURIComponent(JSON.stringify(data));
const url = `https://example.com/?data=${encoded}`;
场景 6:Base64 数据传 URL
const file = btoa('Hello'); // SGVsbG8=
const url = `https://example.com/?data=${encodeURIComponent(file)}`;
// Base64 中的 + 和 / 是特殊字符,必须编码
场景 7:邮件链接
<a href="mailto:foo@example.com?subject=Hello&body=Hello%20World">发送邮件</a>
邮件体中的空格用 %20。
场景 8:锚点链接
<a href="#section-1">跳转</a>
锚点不需要编码(除非含特殊字符)。
URL 各部分编码规则
协议
https://
不编码。
域名
example.com
ASCII 域名不编码。国际化域名用 Punycode:
中文.com → xn--fiq228c.com
端口
:8080
不编码。
路径
/path/to/resource
/ 不编码。其他特殊字符编码。
查询字符串
?key=value&key2=value2
? & = 不编码。值中的这些字符需编码。
锚点
#section
# 不编码。
常见错误
错误 1:双重编码
const url = 'https://example.com/?q=中文';
const encoded1 = encodeURI(url);
const encoded2 = encodeURI(encoded1); // 双重编码
// %2520 而不是 %20
避免重复编码。
错误 2:错用 encodeURI 替代 encodeURIComponent
const value = 'a&b=c';
const url = `?data=${encodeURI(value)}`;
// ?data=a&b=c ← b=c 被当作新参数
const url2 = `?data=${encodeURIComponent(value)}`;
// ?data=a%26b%3Dc ← 正确
错误 3:URL 参数已编码又被自动解码
某些后端框架自动解码 URL 参数,再次解码会出错:
# Flask 自动解码
@app.route('/api')
def api():
q = request.args.get('q') # 已解码
decoded = unquote(q) # 错误:再次解码
错误 4:用 + 替代 %20 在 URL 路径
+ 在 URL 路径中是字面加号,不是空格。空格用 %20。
错误 5:忘记编码中文
错误:window.open('https://example.com/?q=中文')
某些浏览器:自动编码
其他浏览器:报错
显式编码更安全。
与其他工具配合
URL + Base64
工具:Base64 编解码
Base64 数据放 URL 中需 URL 编码(避免 + / = 问题)。
URL + HTML 实体
工具:HTML 实体
HTML 属性中的 URL 可能需 HTML 实体编码:
<a href="https://example.com/?q="hello"">link</a>
URL + JSON
工具:JSON 格式化
JSON 数据放 URL 中需 URL 编码。
常见问题
Q: encodeURI 和 encodeURIComponent 区别?
A: encodeURI 保留 URL 结构字符(;,/?:@&=+$#),用于整个 URL;encodeURIComponent 编码所有特殊字符,用于参数值。
Q: 空格编码成 + 还是 %20?
A: 表单提交(application/x-www-form-urlencoded)用 +,URL 标准用 %20。encodeURIComponent 用 %20。
Q: 中文怎么编码?
A: UTF-8 编码后每字节百分号:中 → UTF-8 E4 B8 AD → %E4%B8%AD。
Q: URL 编码是加密吗?
A: 不是。URL 编码是字符表示转换,不是加密。任何人都能解码。
Q: emoji 怎么编码?
A: UTF-8 编码后百分号。如 😀(U+1F600)UTF-8 F0 9F 98 80 → %F0%9F%98%80。
Q: 工具会泄露我的 URL 吗?
A: 52tool URL 编解码在浏览器端运行,输入不上传服务器。
常见问题解答
Q: 怎么编码 URL 参数?
A: JavaScript: encodeURIComponent('参数值')。Python: urllib.parse.quote('参数值')。或用 52tool URL 编解码。
Q: 怎么解码 URL?
A: JavaScript: decodeURIComponent('%E4%B8%AD%E6%96%87') → '中文'。或用工具。
Q: encodeURI 和 encodeURIComponent 用哪个?
A: 整个 URL 用 encodeURI,URL 参数值用 encodeURIComponent。
Q: URL 编码后的 + 是空格吗?
A: 看上下文。查询字符串中 + 表示空格(表单规范)。URL 路径中 + 是字面加号。
Q: 怎么避免双重编码?
A: 检查字符串是否已含 %。已编码的不再编码。或用专门的"安全编码"函数。
Q: URL 中的中文要编码吗?
A: 现代浏览器自动编码,但建议显式编码确保兼容。后端接收时记得解码。
总结
URL 编解码是前端必备:
- 在线工具 → 52tool URL 编解码
- 整个 URL → encodeURI
- 参数值 → encodeURIComponent
- 解码 → decodeURIComponent
记住:参数值用 encodeURIComponent,中文必须编码,避免双重编码。