URL エンコード・デコード

URL エンコード・デコードツール、URL内の特殊文字を処理

ツール概要:URL エンコード・デコードツール

URL エンコード(パーセントエンコーディング)は、URL内で特殊な意味を持つ文字や、ASCII文字セット以外の文字を安全に転送するためのエンコード方式です。Web開発、API通信、フォーム送信などで広く使用されています。

URL エンコード・デコードツールとは?

URL エンコード・デコードツールはURLパラメータをエンコード/デコードして特殊文字の問題を防ぎます。

使い方

  1. URLやパラメータを貼り付けます。
  2. エンコード/デコードを選択します。
  3. 結果をコピーします。

よくある利用シーン

  • 安全なパラメータ送信
  • URL内の特殊文字のデバッグ
  • APIパラメータの検証

❓ よくある質問

Q1: 文字化けする?
A: UTF-8と二重エンコードを確認してください。

Q2: 空白はどうなる?
A: %20または+に変換されます。

Q3: 一括処理は?
A: 対応していれば複数行入力を使います。

✨ 主な機能

  • 🔄 双方向変換:URLエンコードとデコードの両方をサポート
  • 🌐 多言語対応:日本語、中国語、絵文字などのUnicode文字に対応
  • リアルタイム変換:入力と同時に変換結果を表示
  • 📋 ワンクリックコピー:変換結果を直接コピー可能
  • 🔧 エラー検出:無効なエンコード形式を自動検出

📖 使用例

日本語URLエンコード

入力URL:

https://www.example.com/検索?q=ツールミ

エンコード結果:

https://www.example.com/%E6%A4%9C%E7%B4%A2?q=%E3%83%84%E3%83%BC%E3%83%AB%E3%83%9F

クエリパラメータエンコード

入力パラメータ:

name=田中太郎&[email protected]&message=こんにちは!

エンコード結果:

name=%E7%94%B0%E4%B8%AD%E5%A4%AA%E9%83%8E&email=tanaka%40example.com&message=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%EF%BC%81

特殊文字エンコード

入力テキスト:

Hello World! #特殊文字 @記号 &アンパサンド

エンコード結果:

Hello%20World%21%20%23%E7%89%B9%E6%AE%8A%E6%96%87%E5%AD%97%20%40%E8%A8%98%E5%8F%B7%20%26%E3%82%A2%E3%83%B3%E3%83%91%E3%82%B5%E3%83%B3%E3%83%89

🎯 応用シーン

1. Web開発

フォーム送信とURL構築:

// フォームデータのURLエンコード
const encodeFormData = (formData) => {
  const params = new URLSearchParams();
  
  for (const [key, value] of Object.entries(formData)) {
    params.append(key, value);
  }
  
  return params.toString();
};

// 使用例
const formData = {
  name: '田中太郎',
  email: '[email protected]',
  message: 'お問い合わせ内容です。',
  category: 'サポート'
};

const encodedData = encodeFormData(formData);
console.log('エンコードされたフォームデータ:', encodedData);
// name=%E7%94%B0%E4%B8%AD%E5%A4%AA%E9%83%8E&email=tanaka%40example.com...

// URLの動的構築
const buildSearchURL = (baseUrl, searchParams) => {
  const url = new URL(baseUrl);
  
  for (const [key, value] of Object.entries(searchParams)) {
    url.searchParams.set(key, value);
  }
  
  return url.toString();
};

// 検索URLの構築
const searchUrl = buildSearchURL('https://www.example.com/search', {
  q: 'JavaScript ツール',
  category: 'プログラミング',
  sort: '新着順'
});

console.log('検索URL:', searchUrl);
// https://www.example.com/search?q=JavaScript%20%E3%83%84%E3%83%BC%E3%83%AB&category=...

2. API通信

RESTful API でのパラメータ処理:

// API クライアントクラス
class APIClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
  }

  // GETリクエストでのクエリパラメータ処理
  async get(endpoint, params = {}) {
    const url = new URL(`${this.baseURL}${endpoint}`);
    
    // パラメータを安全にエンコード
    for (const [key, value] of Object.entries(params)) {
      if (value !== null && value !== undefined) {
        url.searchParams.set(key, value);
      }
    }

    try {
      const response = await fetch(url.toString());
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      return await response.json();
    } catch (error) {
      console.error('API リクエストエラー:', error);
      throw error;
    }
  }

  // POSTリクエストでのフォームデータ送信
  async post(endpoint, data) {
    const url = `${this.baseURL}${endpoint}`;
    
    // フォームデータとしてエンコード
    const formData = new URLSearchParams();
    for (const [key, value] of Object.entries(data)) {
      formData.append(key, value);
    }

    try {
      const response = await fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: formData.toString()
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      console.error('API POSTエラー:', error);
      throw error;
    }
  }

  // ファイルアップロード
  async uploadFile(endpoint, file, additionalData = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const formData = new FormData();
    
    // ファイルを追加
    formData.append('file', file);
    
    // 追加データを追加(自動的にエンコードされる)
    for (const [key, value] of Object.entries(additionalData)) {
      formData.append(key, value);
    }

    try {
      const response = await fetch(url, {
        method: 'POST',
        body: formData // Content-Typeは自動設定
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      console.error('ファイルアップロードエラー:', error);
      throw error;
    }
  }
}

// 使用例
const apiClient = new APIClient('https://api.example.com');

// 検索API呼び出し
apiClient.get('/search', {
  q: 'JavaScript ライブラリ',
  category: 'プログラミング',
  page: 1,
  limit: 20
}).then(results => {
  console.log('検索結果:', results);
}).catch(error => {
  console.error('検索エラー:', error);
});

// ユーザー登録
apiClient.post('/users', {
  name: '山田花子',
  email: '[email protected]',
  bio: 'フロントエンド開発者です。'
}).then(user => {
  console.log('ユーザー作成成功:', user);
}).catch(error => {
  console.error('ユーザー作成エラー:', error);
});

3. ブラウザ履歴管理

SPA でのURL状態管理:

// URL状態管理クラス
class URLStateManager {
  constructor() {
    this.currentState = this.parseCurrentURL();
    
    // ブラウザの戻る/進むボタン対応
    window.addEventListener('popstate', (event) => {
      this.currentState = event.state || this.parseCurrentURL();
      this.onStateChange(this.currentState);
    });
  }

  // 現在のURLから状態を解析
  parseCurrentURL() {
    const url = new URL(window.location.href);
    const state = {};
    
    // クエリパラメータを状態として取得
    for (const [key, value] of url.searchParams.entries()) {
      state[key] = decodeURIComponent(value);
    }
    
    return state;
  }

  // 状態をURLに反映
  updateURL(newState, title = '') {
    const url = new URL(window.location.href);
    
    // 既存のクエリパラメータをクリア
    url.search = '';
    
    // 新しい状態をクエリパラメータとして設定
    for (const [key, value] of Object.entries(newState)) {
      if (value !== null && value !== undefined && value !== '') {
        url.searchParams.set(key, value);
      }
    }

    // ブラウザ履歴を更新
    window.history.pushState(newState, title, url.toString());
    this.currentState = newState;
    this.onStateChange(newState);
  }

  // 状態を置換(履歴に追加しない)
  replaceURL(newState, title = '') {
    const url = new URL(window.location.href);
    url.search = '';
    
    for (const [key, value] of Object.entries(newState)) {
      if (value !== null && value !== undefined && value !== '') {
        url.searchParams.set(key, value);
      }
    }

    window.history.replaceState(newState, title, url.toString());
    this.currentState = newState;
  }

  // 状態変更時のコールバック
  onStateChange(state) {
    console.log('URL状態が変更されました:', state);
    // アプリケーションの状態を更新
    this.updateApplicationState(state);
  }

  // アプリケーション状態の更新
  updateApplicationState(state) {
    // 検索フォームの更新
    if (state.q) {
      const searchInput = document.getElementById('searchInput');
      if (searchInput) {
        searchInput.value = state.q;
      }
    }

    // フィルターの更新
    if (state.category) {
      const categorySelect = document.getElementById('categorySelect');
      if (categorySelect) {
        categorySelect.value = state.category;
      }
    }

    // ページネーションの更新
    if (state.page) {
      this.updatePagination(parseInt(state.page));
    }
  }

  // 検索状態の更新
  updateSearchState(query, category = '', page = 1) {
    const newState = {
      q: query,
      category: category,
      page: page > 1 ? page : undefined
    };

    this.updateURL(newState, `検索: ${query}`);
  }

  // 現在の状態を取得
  getCurrentState() {
    return { ...this.currentState };
  }

  // 特定のパラメータを取得
  getParam(key, defaultValue = '') {
    return this.currentState[key] || defaultValue;
  }
}

// 使用例
const urlStateManager = new URLStateManager();

// 検索フォームの処理
document.getElementById('searchForm').addEventListener('submit', (event) => {
  event.preventDefault();
  
  const formData = new FormData(event.target);
  const query = formData.get('q');
  const category = formData.get('category');
  
  // URL状態を更新
  urlStateManager.updateSearchState(query, category, 1);
  
  // 検索実行
  performSearch(query, category, 1);
});

// ページネーション処理
const handlePageChange = (page) => {
  const currentState = urlStateManager.getCurrentState();
  urlStateManager.updateSearchState(
    currentState.q || '',
    currentState.category || '',
    page
  );
  
  performSearch(currentState.q, currentState.category, page);
};

// 検索実行関数
const performSearch = async (query, category, page) => {
  try {
    const results = await apiClient.get('/search', {
      q: query,
      category: category,
      page: page,
      limit: 20
    });
    
    displaySearchResults(results);
  } catch (error) {
    console.error('検索エラー:', error);
  }
};

🔧 技術詳細

エンコード規則

URL エンコードの基本規則:

予約文字:

  • :%3A
  • /%2F
  • ?%3F
  • #%23
  • [%5B
  • ]%5D
  • @%40

非予約文字:

  • A-Z, a-z, 0-9 → そのまま
  • -, ., _, ~ → そのまま

その他の文字:

  • UTF-8バイト列に変換後、各バイトを %XX 形式でエンコード

JavaScript での実装

// 標準的なエンコード/デコード関数
const urlEncode = (str) => {
  return encodeURIComponent(str);
};

const urlDecode = (str) => {
  try {
    return decodeURIComponent(str);
  } catch (error) {
    console.error('デコードエラー:', error);
    return str; // デコードに失敗した場合は元の文字列を返す
  }
};

// カスタムエンコード関数(より細かい制御)
const customUrlEncode = (str, encodeSpaceAsPlus = false) => {
  let encoded = encodeURIComponent(str);
  
  if (encodeSpaceAsPlus) {
    encoded = encoded.replace(/%20/g, '+');
  }
  
  return encoded;
};

// 安全なデコード関数
const safeUrlDecode = (str) => {
  try {
    // + を空白に変換(フォームデータの場合)
    const normalized = str.replace(/\+/g, ' ');
    return decodeURIComponent(normalized);
  } catch (error) {
    console.error('デコードエラー:', error);
    return str;
  }
};

💡 使用のコツ

  • 適切な関数選択encodeURIComponent()encodeURI() の使い分け
  • エラーハンドリング:デコード時の例外処理を忘れずに
  • 文字エンコーディング:UTF-8エンコーディングを前提とする
  • テスト:特殊文字を含むデータで十分にテストする

⚠️ 注意事項

  • 二重エンコード:既にエンコードされた文字列を再度エンコードしないよう注意
  • デコードエラー:無効なエンコード文字列のデコード時は例外が発生する可能性
  • 文字制限:一部のシステムではURL長に制限がある
  • セキュリティ:ユーザー入力をURLに含める際は適切な検証を行う

🚀 使い方

  1. テキスト入力:エンコード・デコードしたいテキストやURLを入力
  2. 操作選択:「エンコード」または「デコード」ボタンをクリック
  3. 結果確認:変換結果が出力エリアに表示される
  4. コピー使用:「コピー」ボタンで結果をクリップボードにコピー
  5. エラー確認:無効な形式の場合はエラーメッセージが表示される

ヒント:このツールはクライアント側でローカル処理を行い、入力データをサーバーに送信しないため、プライバシーが保護されます。