JavaScript約7分で読めます

JavaScriptでURLをデコードする方法(完全ガイド)

JavaScriptには、個々のURIコンポーネントをデコードする decodeURIComponent() と、URI全体をデコードする decodeURI() が用意されています。不正なURIを扱うには try-catch を使いましょう。URLやURLSearchParams APIは、URLを解析するためのより安全な代替手段を提供します。

decodeURIComponent() — URIコンポーネントをデコードする

decodeURIComponent() は、JavaScriptでURLエンコードされた文字列をデコードするための主要な関数です。すべてのパーセントエンコードされたシーケンスをデコードし、元の文字に戻します。クエリパラメータの値やパスセグメント、あるいはURIの単一コンポーネントをデコードするときは、この関数を使いましょう。

// 基本的なデコード
console.log(decodeURIComponent('hello%20world'));
// "hello world"

console.log(decodeURIComponent('price%3D10%26qty%3D2'));
// "price=10&qty=2"

// Unicode文字のデコード
console.log(decodeURIComponent('caf%C3%A9'));
// "cafe"(アクセント付き)

console.log(decodeURIComponent('%E4%B8%AD%E6%96%87'));
// 中国語の文字

// URLからクエリパラメータの値をデコードする
const url = 'https://example.com/search?q=C%2B%2B%20%26%20Java';
const params = url.split('?')[1];
const value = params.split('=')[1];
console.log(decodeURIComponent(value));
// "C++ & Java"

decodeURIComponent() は、%2F(/)や %3F(?)といった予約文字を表すものも含め、すべてのパーセントエンコードされたシーケンスをデコードします。これは個々のURIコンポーネントを扱う場合には正しい動作ですが、URL全体に適用すると問題を引き起こす可能性があります。

decodeURI() — URI全体をデコードする

decodeURI() は、URIの構造を保ちながらURI全体をデコードします。decodeURIComponent() とは異なり、%2F(/)、%3F(?)、%23(#)、%26(&)のような予約されたURI文字を表すシーケンスはデコードしません

// decodeURI はURIの構造を保つ
console.log(decodeURI('https://example.com/my%20page?q=hello%20world'));
// "https://example.com/my page?q=hello world"
// スペースはデコードされるが、/、?、= は保持される

// 完全なURLに対する decodeURIComponent との比較
console.log(decodeURIComponent('https%3A%2F%2Fexample.com%2Fpath'));
// "https://example.com/path" - URL全体がエンコードされていた場合は正しくデコードされる

// decodeURI は予約文字のシーケンスをデコードしない
console.log(decodeURI('path%2Fto%2Ffile'));
// "path%2Fto%2Ffile" - / は予約文字なので %2F はデコードされない
console.log(decodeURIComponent('path%2Fto%2Ffile'));
// "path/to/file" - %2F はデコードされる

URLの構造を変えずに(たとえば表示目的で)読みやすくしたい場合は decodeURI() を使いましょう。ほとんどのプログラム上のユースケースでは、個々のコンポーネントに decodeURIComponent() を適用するのが適切です。

不正なURIを扱う

decodeURI()decodeURIComponent() はどちらも、無効なパーセントエンコードシーケンスに遭遇すると URIError をスローします。これは、単独のパーセント記号、不完全なシーケンス、無効なUTF-8バイトシーケンスなどで発生します。ユーザーが入力したURLや外部から取得したURLを扱うときは、常にデコード処理を try-catch ブロックで囲みましょう。

// これらは URIError: URI malformed をスローする
try {
  decodeURIComponent('%');         // 単独のパーセント記号
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

try {
  decodeURIComponent('%2');        // 不完全なシーケンス
} catch (e) {
  console.error(e.message);       // "URI malformed"
}

// 安全にデコードする関数
function safeDecode(str) {
  try {
    return decodeURIComponent(str);
  } catch (e) {
    console.warn('Failed to decode:', str);
    return str; // 失敗した場合は元の文字列を返す
  }
}

// デコード前に不正なパーセントシーケンスを修正する
function fixAndDecode(str) {
  // 単独の % を %25(エンコードされたパーセント記号)に置き換える
  const fixed = str.replace(/%(?![0-9A-Fa-f]{2})/g, '%25');
  return decodeURIComponent(fixed);
}

console.log(fixAndDecode('100% complete'));
// "100% complete"

URL APIを使う(推奨)

モダンな URL および URLSearchParams APIは、URLを解析・デコードするためのより安全で構造化された方法を提供します。エンコードとデコードを自動的に処理してくれるため、エラーのリスクを軽減できます。

// URLを解析してそのコンポーネントにアクセスする(自動的にデコードされる)
const url = new URL('https://example.com/path%20here?q=hello%20world&lang=en');

console.log(url.pathname);  // "/path here"(デコード済み)
console.log(url.search);    // "?q=hello%20world&lang=en"(生の値)

// URLSearchParams はパラメータの値を自動的にデコードする
console.log(url.searchParams.get('q'));     // "hello world"
console.log(url.searchParams.get('lang')); // "en"

// すべてのパラメータを反復処理する
for (const [key, value] of url.searchParams) {
  console.log(key, '=', value);
}
// q = hello world
// lang = en

// URLSearchParams は + をスペースとして扱う(フォームエンコード)
const formParams = new URLSearchParams('q=hello+world&lang=en');
console.log(formParams.get('q'));  // "hello world"

// 自動エンコードでURLを組み立てる
const newUrl = new URL('https://example.com/search');
newUrl.searchParams.set('q', 'C++ & Java');
newUrl.searchParams.set('page', '1');
console.log(newUrl.toString());
// "https://example.com/search?q=C%2B%2B+%26+Java&page=1"

よくあるデコードの間違い

間違い1: decodeURIComponent() でURL全体をデコードする。 エンコードされた予約文字が含まれている場合、URLの構造が壊れることがあります。クエリ値の中の %2F/ に変わり、URLの意味が変わってしまう可能性があります。

間違い2: 二重デコード。 一度デコードされた文字列を再度デコードすると、予期しない結果やエラーが生じることがあります。たとえば、文字列 %2520 はまず %20 にデコードされ、次にスペースにデコードされます。エンコードが1段階だけだと想定している場合、二重デコードはデータを破壊してしまいます。

// 二重デコードの問題
const encoded = '%2520'; // これはエンコードされた %20
console.log(decodeURIComponent(encoded));  // "%20"(正しい - 1段階)
console.log(decodeURIComponent(decodeURIComponent(encoded)));  // " "(二重にデコードされた!)

// デコードする前に、その文字列がデコードを必要とするか確認する
function needsDecoding(str) {
  return str !== decodeURIComponent(str);
}

間違い3: + 記号を扱わない。 decodeURIComponent()+ をスペースに変換しません。form-urlencodedデータをデコードする場合は、先に + をスペースに置き換えるか、これを自動的に処理する URLSearchParams を使う必要があります。

// decodeURIComponent は + をスペースとしてデコードしない
console.log(decodeURIComponent('hello+world'));
// "hello+world"("hello world" ではない!)

// 対処法: デコードする前に + を置き換える
function decodeFormValue(str) {
  return decodeURIComponent(str.replace(/\+/g, ' '));
}
console.log(decodeFormValue('hello+world'));
// "hello world"

// または URLSearchParams を使う(+ を自動的に処理する)
const params = new URLSearchParams('q=hello+world');
console.log(params.get('q'));
// "hello world"

関連記事

無料ツールを試す