文字列をURLエンコードするには
日本語や記号を含む文字列を URL に安全に載せるためのパーセントエンコードを各言語で示す。
スペースが + になるか %20 になるかの違いと、値用とURL全体用の関数の使い分けまでを扱う。
公開:
選んだ言語は、ほかのサンプルコードを開いたときも最初に表示する。
Python
できる · 実行確認済みfrom urllib.parse import quote, quote_plus, urlencode
keyword = "東京 駅&a=b"
print(quote(keyword, safe="")) # %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
print(quote_plus(keyword)) # %E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db(スペースは +)
print(quote("/api/users/a b")) # /api/users/a%20b(safe の既定は "/" なのでそのまま残る)
params = {"q": keyword, "page": 2}
print("https://example.com/search?" + urlencode(params))
# https://example.com/search?q=%E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db&page=2urllib.parse.quote は safe 引数に残したい文字を渡す仕組みで、既定の safe は "/" なので、クエリの値に使うなら safe="" を明示してスラッシュも変換する。
スペースを + にする quote_plus はフォーム形式向けで、辞書をまとめて組み立てるなら urlencode を使う。
JavaScript
できる · 実行確認済みconst keyword = "東京 駅&a=b";
console.log(encodeURIComponent(keyword)); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
console.log(encodeURI(keyword)); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85&a=b(& と = は残る)
const url = new URL("https://example.com/search");
url.searchParams.set("q", keyword);
url.searchParams.set("page", "2");
console.log(url.toString());
// https://example.com/search?q=%E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db&page=2(スペースは +)クエリの値や単独のパラメータには encodeURIComponent を使う。
encodeURI は URL 全体を渡す前提で & や = を残すため、値に使うと区切り文字が混ざる。
複数のパラメータを組み立てるなら URLSearchParams に任せるのが安全である。
TypeScript
できる · 実行確認済みconst keyword: string = "東京 駅&a=b";
function buildSearchUrl(base: string, params: Record<string, string | number>): string {
const url = new URL(base);
for (const [key, value] of Object.entries(params)) {
url.searchParams.set(key, String(value));
}
return url.toString();
}
console.log(encodeURIComponent(keyword)); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
console.log(buildSearchUrl("https://example.com/search", { q: keyword, page: 2 }));
// https://example.com/search?q=%E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db&page=2パラメータを Record で受け取って URL に流し込む関数にしておくと、呼び出しごとに encodeURIComponent を書き忘れる事故を防げる。
実行時の振る舞いは JavaScript と同じで、数値は String に変換してから渡す。
Go
できる · 実行確認済みpackage main
import (
"fmt"
"net/url"
)
func main() {
keyword := "東京 駅&a=b"
fmt.Println(url.QueryEscape(keyword)) // %E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db(クエリ用。スペースは +)
fmt.Println(url.PathEscape(keyword)) // %E6%9D%B1%E4%BA%AC%20%E9%A7%85&a=b(パス用。スペースは %20)
values := url.Values{}
values.Set("q", keyword)
values.Set("page", "2")
fmt.Println("https://example.com/search?" + values.Encode())
// https://example.com/search?page=2&q=%E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db(キー順に並ぶ)
}クエリ用の QueryEscape はスペースを + にし、パス用の PathEscape は %20 にする。
PathEscape は & や = を変換しないため、クエリの値には使わない。
複数のパラメータは url.Values にまとめて Encode するとキー順に並ぶ。
Rust
できる · 実行確認済み// Cargo.toml: urlencoding = "2"
fn main() {
let keyword = "東京 駅&a=b";
let encoded = urlencoding::encode(keyword);
println!("{}", encoded); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
let decoded = urlencoding::decode(&encoded).unwrap();
println!("{}", decoded); // 東京 駅&a=b
}標準ライブラリに URL エンコードの関数が無いため、urlencoding クレートを Cargo.toml に追加して使う。
encode は英数字と - _ . ~ 以外をすべて変換するので、値の部分にそのまま使える。
依存を書き忘れると E0433 で解決できない。
Java
できる · 実行確認済みimport java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) {
String keyword = "東京 駅&a=b";
String encoded = URLEncoder.encode(keyword, StandardCharsets.UTF_8);
System.out.println(encoded); // %E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db(スペースは +)
System.out.println(encoded.replace("+", "%20")); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
}
}URLEncoder.encode は文字コードを必ず明示する。
引数が文字列だけの旧メソッドは非推奨で、javac の -Xlint:deprecation で警告が出る。
出力はフォーム形式でスペースが + になるため、%20 が必要なときは置き換える。
C#
できる · 実行確認済みusing System;
using System.Net;
var keyword = "東京 駅&a=b";
Console.WriteLine(Uri.EscapeDataString(keyword)); // %E6%9D%B1%E4%BA%AC%20%E9%A7%85%26a%3Db
Console.WriteLine(WebUtility.UrlEncode(keyword)); // %E6%9D%B1%E4%BA%AC+%E9%A7%85%26a%3Db(スペースは +)Uri.EscapeDataString はスペースを %20 にし、WebUtility.UrlEncode は + にする。
クエリの値には、% 形式で統一して変換する EscapeDataString を選ぶと、受け側との食い違いが起きにくい。
つまずき
同じ「URL エンコード」でも、スペースの変換結果が言語と関数で分かれる。
実行して比べると、Python の quote、JavaScript の encodeURIComponent、Go の PathEscape、Rust の urlencoding、C# の Uri.EscapeDataString は %20 を返し、Python の quote_plus、Go の QueryEscape、Java の URLEncoder、C# の WebUtility.UrlEncode と各言語の URLSearchParams は + を返した。
+ はフォーム形式(application/x-www-form-urlencoded)の表記で、クエリ文字列では通じるがパスの途中ではただの + 記号として扱われる。
送り先がどちらを期待するかを先に確かめ、揃っていないときは変換後に置き換える。
値に使う関数と URL 全体に使う関数は別物
JavaScript の encodeURI は URL 全体を渡す前提の関数で、& や = を変換せずに残す。
「東京 駅&a=b」を渡すと実行結果は %E6%9D%B1%E4%BA%AC%20%E9%A7%85&a=b となり、値の中の & がそのまま区切り文字として働いてしまう。
クエリの値は encodeURIComponent に通すと %26a%3Db まで変換される。
Go の PathEscape も同じ理由で & と = を残すので、値には QueryEscape を使う。
Python の quote は safe の既定が "/" のため、スラッシュを含む文字列は safe="" を指定しない限りそのまま残る。
変換されない記号の違い
URLSearchParams と encodeURIComponent は、変換しない記号が異なる。
実行すると encodeURIComponent は a!b~c*d(e)f をそのまま返したが、URLSearchParams は a!b~c*d の ! を %21、~ を %7E に変換し、* だけを残した。
Java の URLEncoder も ~ を %7E に変換して * を残す。
Python の quote は ~ を残し、! と * を変換した。
どの記号が残るかは関数ごとに違うので、変換結果を文字列として比較する場面では、生成側と受け側で同じ関数を使う。