DevToolBox免费
博客

URL 编码与解码完全指南:百分号编码详解

10 分钟阅读作者 DevToolBox

URL 编码(也称为百分号编码)是 Web 开发中最基本的机制之一。每当你提交表单、点击带有查询参数的链接或调用 REST API 时,URL 编码URL 解码操作都在幕后进行。无论你需要一个快速的 URL 编码器来处理一次性任务,还是想在线进行 URL 编码解码,本综合指南涵盖了从 RFC 3986 规范到 JavaScript、Python、Bash、PHP 和 Java 实用代码示例的所有内容。

立即试用我们的免费在线 URL 编码/解码工具。

什么是 URL 编码(百分号编码)?

URL 编码在 RFC 3986 中正式定义,是一种通过将不安全或保留字符替换为百分号(%)后跟两个十六进制数字来编码 URI 信息的机制。例如,空格字符(ASCII 32,十六进制 20)变为 %20。由于每个编码字符都以 % 符号开头,因此这个过程也被称为百分号编码

URL 只能包含 US-ASCII 字符集中的有限字符集。不需要编码的 URL 安全字符包括:大写字母(A-Z)、小写字母(a-z)、数字(0-9)和四个特殊字符:连字符(-)、下划线(_)、句点(.)和波浪号(~)。在 RFC 3986 中这些被称为"非保留字符"。其他所有字符都必须进行百分号编码。

为什么 URL 需要编码?URL 作为 Web 上的通用地址,必须是明确的。&=?# 等字符在 URL 中有特殊的结构含义。如果你的数据包含这些字符,就必须进行编码,以便浏览器和服务器能够区分数据和 URL 结构。

URL 编码工作原理

URL 编码过程遵循简单的算法。对于每个需要编码的字符:

  1. 使用 UTF-8 编码将字符转换为字节表示。ASCII 字符产生单个字节;非 ASCII 字符产生 2-4 个字节。
  2. 对于每个字节,写一个百分号(%)后跟该字节的两位大写十六进制值。
  3. 非保留字符A-Z a-z 0-9 - _ . ~)保持原样不编码。
  4. 保留字符: / ? # [ ] @ ! $ & ' ( ) * + , ; =)仅在会被误解时才编码。

示例:编码字符串 Hello World! Price: $5 产生 Hello%20World%21%20Price%3A%20%245

UTF-8 多字节示例:中文字符"中"(U+4E2D)编码为三个 UTF-8 字节 0xE4 0xB8 0xAD,产生 %E4%B8%AD。URL 解码器通过读取十六进制值、重建字节并转换回 UTF-8 字符来逆转此过程。

URL 编码参考表

以下是常用编码字符的完整参考表。收藏此表以便在需要 URL 编码特殊字符时快速查找:

CharacterEncodedDescription
(space)%20Space
!%21Exclamation mark
#%23Hash / Fragment
$%24Dollar sign
%%25Percent sign
&%26Ampersand
'%27Apostrophe
(%28Left parenthesis
)%29Right parenthesis
*%2AAsterisk
+%2BPlus sign
,%2CComma
/%2FForward slash
:%3AColon
;%3BSemicolon
=%3DEquals sign
?%3FQuestion mark
@%40At sign
[%5BLeft bracket
]%5DRight bracket
{%7BLeft brace
}%7DRight brace
|%7CPipe
<%3CLess than
>%3EGreater than
"%22Double quote

空格编码:%20 与 +(加号)

URL 编码中最令人困惑的方面之一是空格的处理方式。有两种有效的方式来编码空格字符,使用哪种取决于上下文:

%20(百分号编码):这是 RFC 3986 为 URI 定义的标准编码。在 URL 路径、查询参数名或任何通用 URI 组件中编码空格时使用 %20

+(加号):此编码由 application/x-www-form-urlencoded 内容类型规范定义(HTML 表单使用)。HTML 表单提交时,表单值中的空格被编码为 +

何时使用哪个?如果你是以编程方式构建 URL(API 调用、REST 端点),始终使用 %20。如果你专门为表单编码格式编码数据,+ 编码会自动使用。

// Space encoding comparison

// RFC 3986 (URI standard) — space = %20
"hello world"  →  "hello%20world"
// Used in: URL paths, API endpoints, general URIs

// application/x-www-form-urlencoded — space = +
"hello world"  →  "hello+world"
// Used in: HTML form submissions, URLSearchParams

// JavaScript comparison:
encodeURIComponent("hello world")  // "hello%20world"  (%20)
new URLSearchParams({q: "hello world"}).toString()  // "q=hello+world"  (+)

// Python comparison:
urllib.parse.quote("hello world")      # "hello%20world"  (%20)
urllib.parse.urlencode({"q": "hello world"})  # "q=hello+world"  (+)

// PHP comparison:
rawurlencode("hello world")  // "hello%20world"  (%20)
urlencode("hello world")     // "hello+world"    (+)

URL 编码/解码代码示例

JavaScript URL 编码/解码

JavaScript 提供了多个内置 URL 编码函数。最重要的区别是 encodeURIComponentencodeURI。理解何时使用每个对于正确的 URL 编码操作至关重要:

// ===== encodeURIComponent vs encodeURI =====

// encodeURIComponent — encodes EVERYTHING except: A-Z a-z 0-9 - _ . ~
// Use for: individual query parameter values, path segments
encodeURIComponent("hello world & goodbye");
// "hello%20world%20%26%20goodbye"

encodeURIComponent("price=100&category=books");
// "price%3D100%26category%3Dbooks"

// encodeURI — preserves URL structure characters: : / ? # & = @ + $
// Use for: encoding a complete URL
encodeURI("https://example.com/search?q=hello world&lang=en");
// "https://example.com/search?q=hello%20world&lang=en"

// WARNING: Do NOT use encodeURIComponent on full URLs!
encodeURIComponent("https://example.com/path");
// "https%3A%2F%2Fexample.com%2Fpath"  ← BROKEN URL!

// ===== Decoding =====
decodeURIComponent("hello%20world%20%26%20goodbye");
// "hello world & goodbye"

decodeURI("https://example.com/search?q=hello%20world");
// "https://example.com/search?q=hello world"

// ===== URLSearchParams (handles form encoding automatically) =====
const params = new URLSearchParams({
  query: "cats & dogs",
  page: "1",
  filter: "price > 50"
});
params.toString();
// "query=cats+%26+dogs&page=1&filter=price+%3E+50"
// Note: spaces become + (form encoding), & in values becomes %26

// Parse query string
const parsed = new URLSearchParams("?q=hello+world&lang=en");
parsed.get("q");  // "hello world" (+ decoded to space)
parsed.get("lang");  // "en"

// ===== Building URLs with the URL API =====
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "hello world & more");
url.searchParams.set("limit", "10");
url.toString();
// "https://api.example.com/search?q=hello+world+%26+more&limit=10"

Python URL 编码/解码

Python 的 urllib.parse 模块提供全面的 URL 编码和解码函数。requests 库会自动处理 API 调用的编码:

import urllib.parse

# ===== quote / unquote (RFC 3986 percent-encoding) =====

# Encode a string (spaces become %20)
urllib.parse.quote("hello world & goodbye")
# "hello%20world%20%26%20goodbye"

# By default, / is NOT encoded (safe="/")
urllib.parse.quote("/path/to/file name.txt")
# "/path/to/file%20name.txt"

# Encode everything including /
urllib.parse.quote("/path/to/file", safe="")
# "%2Fpath%2Fto%2Ffile"

# Decode
urllib.parse.unquote("hello%20world%20%26%20goodbye")
# "hello world & goodbye"

# ===== urlencode (for query strings, uses + for spaces) =====
params = {"q": "cats & dogs", "page": 1, "filter": "price > 50"}
urllib.parse.urlencode(params)
# "q=cats+%26+dogs&page=1&filter=price+%3E+50"

# ===== quote_plus (form encoding, spaces become +) =====
urllib.parse.quote_plus("hello world")
# "hello+world"

urllib.parse.unquote_plus("hello+world")
# "hello world"

# ===== With the requests library =====
import requests

# requests handles URL encoding automatically
response = requests.get(
    "https://api.example.com/search",
    params={"q": "hello world", "lang": "en"}
)
# Actual URL: https://api.example.com/search?q=hello+world&lang=en

# For path segments, encode manually
username = "john@example.com"
url = f"https://api.example.com/users/{urllib.parse.quote(username, safe='')}"
# "https://api.example.com/users/john%40example.com"

Bash / curl URL 编码/解码

使用 curl 或 shell 脚本时,经常需要对 HTTP 请求的数据进行 URL 编码。--data-urlencode 参数是最简单的方法:

# ===== curl --data-urlencode =====

# GET request with URL-encoded query parameter
curl -G "https://api.example.com/search" \
  --data-urlencode "q=hello world & more" \
  --data-urlencode "lang=en"
# Request URL: /search?q=hello%20world%20%26%20more&lang=en

# POST with form-encoded body
curl -X POST "https://api.example.com/submit" \
  --data-urlencode "name=John Doe" \
  --data-urlencode "message=Hello! How are you?"

# ===== Pure Bash URL encoding (using printf) =====
urlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o
  for (( pos=0 ; pos<strlen ; pos++ )); do
    c=${string:$pos:1}
    case "$c" in
      [-_.~a-zA-Z0-9]) o="${c}" ;;
      *) printf -v o '%%%02X' "'$c" ;;
    esac
    encoded+="${o}"
  done
  echo "${encoded}"
}

urlencode "hello world & goodbye"
# "hello%20world%20%26%20goodbye"

# ===== Using Python one-liner in Bash =====
python3 -c "import urllib.parse; print(urllib.parse.quote('hello world'))"
# "hello%20world"

# ===== Decode URL-encoded strings =====
python3 -c "import urllib.parse; print(urllib.parse.unquote('hello%20world'))"
# "hello world"

PHP URL 编码/解码

PHP 提供了两对 URL 编码函数。理解 urlencode()rawurlencode() 的区别对于正确的百分号编码至关重要:

<?php
// ===== urlencode / urldecode =====
// Uses + for spaces (application/x-www-form-urlencoded)

echo urlencode("hello world & goodbye");
// "hello+world+%26+goodbye"

echo urldecode("hello+world+%26+goodbye");
// "hello world & goodbye"

// ===== rawurlencode / rawurldecode =====
// Uses %20 for spaces (RFC 3986)

echo rawurlencode("hello world & goodbye");
// "hello%20world%20%26%20goodbye"

echo rawurldecode("hello%20world%20%26%20goodbye");
// "hello world & goodbye"

// ===== When to use which? =====
// urlencode()    → for query string values (form-style)
// rawurlencode() → for URL path segments (RFC 3986)

// Building a URL with encoded path and query
$path = rawurlencode("my file.pdf");
$query = urlencode("search term & more");
$url = "https://example.com/files/{$path}?q={$query}";
// "https://example.com/files/my%20file.pdf?q=search+term+%26+more"

// ===== http_build_query (encode arrays as query strings) =====
$params = [
    "q" => "cats & dogs",
    "page" => 1,
    "tags" => ["php", "url encoding"]
];
echo http_build_query($params);
// "q=cats+%26+dogs&page=1&tags%5B0%5D=php&tags%5B1%5D=url+encoding"
?>

Java URL 编码/解码

Java 的 URLEncoderURLDecoder 类处理 URL 编码和解码操作。始终显式指定 UTF-8 编码:

import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

// ===== URLEncoder.encode (uses + for spaces) =====

// Always specify UTF-8 charset!
String encoded = URLEncoder.encode(
    "hello world & goodbye", StandardCharsets.UTF_8
);
// "hello+world+%26+goodbye"

// Decode
String decoded = URLDecoder.decode(
    "hello+world+%26+goodbye", StandardCharsets.UTF_8
);
// "hello world & goodbye"

// ===== Convert + to %20 for RFC 3986 compliance =====
String rfc3986 = URLEncoder.encode("hello world", StandardCharsets.UTF_8)
    .replace("+", "%20");
// "hello%20world"

// ===== Java 11+ URI encoding =====
import java.net.URI;

URI uri = URI.create("https://example.com/search");
// For building URLs with encoded parameters:
String query = "q=" + URLEncoder.encode("hello world", StandardCharsets.UTF_8);
URI fullUri = URI.create("https://example.com/search?" + query);
// https://example.com/search?q=hello+world

// ===== Encoding path segments =====
String pathSegment = URLEncoder.encode("my file.pdf", StandardCharsets.UTF_8)
    .replace("+", "%20");
String url = "https://example.com/files/" + pathSegment;
// "https://example.com/files/my%20file.pdf"

常见 URL 编码错误

即使是经验丰富的开发者也会犯这些 URL 编码错误。以下是需要避免的常见陷阱:

双重编码:当你对已编码的字符串再次编码时就会发生。例如,%20 变成 %2520。解决方法:始终编码原始值,不要将已编码的字符串再次通过编码器。

// Double encoding example — a common bug!

const value = "hello world";

// Correct: encode once
const correct = encodeURIComponent(value);
// "hello%20world" ✓

// Bug: encode twice
const doubleEncoded = encodeURIComponent(encodeURIComponent(value));
// "hello%2520world" ✗  (%25 is the encoded form of %)

// How to detect double encoding:
// If you see %25 in your URLs, you likely have a double encoding issue
// %2520 = double-encoded space (%25 = %, 20 = space)
// %253A = double-encoded colon (%25 = %, 3A = :)

编码整个 URL:将整个 URL 通过 encodeURIComponent() 是常见错误。这会编码斜杠、冒号等结构字符,破坏 URL。应只编码单个参数值。

// Encoding the entire URL — WRONG!
const url = "https://example.com/path?q=hello world";

// WRONG: breaks URL structure
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fq%3Dhello%20world"

// CORRECT option 1: use encodeURI for full URLs
encodeURI(url);
// "https://example.com/path?q=hello%20world"

// CORRECT option 2: encode only the value
const base = "https://example.com/path";
const query = encodeURIComponent("hello world");
const fullUrl = base + "?q=" + query;
// "https://example.com/path?q=hello%20world"

忘记 UTF-8:某些语言(尤其是 Java 和旧版 PHP)的默认编码可能不是 UTF-8。始终使用 UTF-8 进行 URL 编码。

未编码路径段:开发者有时忘记 URL 路径段也需要编码。文件路径如 /documents/my report.pdf 必须变为 /documents/my%20report.pdf

错误处理 + 号:在所有上下文中将 + 视为空格是错误的。在表单编码格式中 + 表示空格,但在常规 URL 路径中 + 是字面加号。

API 和 Web 应用中的 URL 编码

在使用 API 和 Web 应用时,理解如何正确进行 URL 编码至关重要。以下是关键场景:

查询参数:构建带查询参数的 API URL 时,每个参数名和值必须单独编码。大多数 HTTP 客户端库会自动处理。

路径参数:嵌入 URL 路径中的值也必须编码。路径段应使用标准百分号编码,不要使用 + 表示空格。

表单数据:发送 application/x-www-form-urlencoded 数据时,编码规则与标准 URL 编码不同。空格编码为 +

JSON 负载:发送 JSON 请求体时,不需要对 JSON 内容进行 URL 编码。但如果需要将 JSON 作为查询参数值传递,整个 JSON 字符串必须进行 URL 编码。

// API URL encoding examples

// Query parameters — encode each value individually
const searchTerm = 'price > 100 & category = "electronics"';
const apiUrl = `https://api.example.com/search?q=${encodeURIComponent(searchTerm)}&limit=10`;
// "https://api.example.com/search?q=price%20%3E%20100%20%26%20...&limit=10"

// Path parameters — encode values embedded in paths
const username = "john@example.com";
const profileUrl = `https://api.example.com/users/${encodeURIComponent(username)}/profile`;
// "https://api.example.com/users/john%40example.com/profile"

// Form data (POST) — uses + for spaces
const formBody = new URLSearchParams({
  username: "john doe",
  password: "p@ss w0rd!"
}).toString();
// "username=john+doe&password=p%40ss+w0rd%21"

// JSON as query parameter — encode the entire JSON string
const filters = JSON.stringify({ price: { min: 10, max: 100 } });
const url = `/api/products?filters=${encodeURIComponent(filters)}`;
// "/api/products?filters=%7B%22price%22%3A%7B%22min%22%3A10%2C%22max%22%3A100%7D%7D"

常见问题

什么是 URL 编码,为什么需要它?

URL 编码(百分号编码)是将字符转换为可在 URL 中安全传输的格式的过程。它用百分号后跟两个十六进制数字替换不安全字符(如空格变为 %20)。URL 只能包含有限的 ASCII 字符集,&、=、?、# 和空格等特殊字符在 URL 中有结构含义,作为数据使用时必须编码以防止误解。非 ASCII 字符(Unicode、中日韩文字、表情符号)也必须先进行 UTF-8 编码再百分号编码。

encodeURI 和 encodeURIComponent 有什么区别?

encodeURI() 编码完整 URI,保留 URL 中有特殊含义的字符如 :、/、?、#、& 和 =。encodeURIComponent() 编码除非保留字符外的所有内容。对完整 URL 使用 encodeURIComponent 会破坏 URL 结构。对单个查询参数值使用 encodeURIComponent,对完整 URL 使用 encodeURI。

URL 中空格应该用 %20 还是 +?

在 URL 路径和编程构建 URL 时使用 %20(RFC 3986 标准)。仅在 application/x-www-form-urlencoded 内容中使用 +(HTML 表单提交格式)。JavaScript 的 encodeURIComponent() 产生 %20,URLSearchParams 使用 +。如有疑问,%20 是更安全且更通用的选择。

URL 编码是每个开发者日常遇到的基础 Web 开发技能。从构建 API 调用到处理表单提交,理解如何正确进行 URL 编码和解码可以防止 bug、安全漏洞和链接失效。收藏本指南以备参考,并使用我们的免费在线工具进行即时编码和解码。

使用我们的免费在线工具即时 URL 编码和解码字符串。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

%20URL Encoder/Decoder🔗URL Parser%←URL Decoder%%Percent Encoding Tool

相关文章

URL 编码特殊字符:完整参考表与示例

URL 百分号编码完整参考。所有特殊字符查找表、encodeURIComponent 与 encodeURI 的使用场景,以及常见编码错误。

Base64 编码与解码完全指南:附代码示例

免费在线 Base64 编码解码工具。详解 Base64 原理,支持 JavaScript、Python、Bash、PowerShell 代码示例。

URL 编码解码在线指南:百分比编码、RFC 3986 与最佳实践

URL 编码(百分比编码)完整指南。涵盖 RFC 3986 保留与非保留字符、encodeURIComponent 与 encodeURI、Python urllib.parse、Java URLEncoder、常见编码字符、表单编码、API 查询参数、双重编码调试与 URL 安全 Base64。