手軽屋
ツール一覧

URL文字化けの修復フロー — Shift_JIS時代の遺産を解読する

メルマガ解除URLや古いCMSのリンクで「%82%B1%82%F1%82%C9%82%BF%82%CD」のような文字列に出会ったら、Shift_JISでエンコードされた「こんにちは」かもしれません。UTF-8で復号する手軽屋のツールではそのままだと壊れた文字が出るので、修復手順を順を追って解説します。

1. なぜ文字化けが起きるのか

パーセントエンコーディング自体は「バイト列を%XXに変換するだけ」のシンプルな仕組み。問題は元の文字をバイト列にする時のエンコーディング(文字符号化方式)。RFC 3986はUTF-8を強く推奨していますが、現実には日本国内のサイトで2010年頃までShift_JISがよく使われていて、その時代の生成URLが今もメルマガ・ブックマーク・古い記事リンクで生き残っています。

手軽屋のツールはUTF-8で復号するため、Shift_JIS由来の%XX列は「変換できませんでした」または文字化けで返ってきます。

2. UTF-8とShift_JISの見分け方(バイトパターン)

%表現の最初の2バイトを見れば、ほぼ判別できます:

最初の%が3つ続いて%E3などが多い → UTF-8、2バイト周期で%82が多い → Shift_JIS、%A4が多い → EUC-JP、と当たりがつきます。

3. ターミナル(macOS/Linux)での修復手順

シェルでさっと変換するならnkf(漢字フィルタ)が定番:

# 1. %XXをバイトに戻す(python3使用)
echo "%82%B1%82%F1%82%C9%82%BF%82%CD" | python3 -c \
  "import sys,urllib.parse; sys.stdout.buffer.write(urllib.parse.unquote_to_bytes(sys.stdin.read()))" \
  > /tmp/sjis.bin

# 2. nkfでShift_JIS→UTF-8に変換
nkf -w /tmp/sjis.bin
# → こんにちは

nkf -wはUTF-8出力。自動判別なので、EUC-JPでもShift_JISでも同じコマンドで通ります。

4. ブラウザのJavaScript(TextDecoder)で復号

ブラウザのTextDecoderはShift_JIS・EUC-JPもサポート(Encoding Living Standard):

// %XXをUint8Arrayに戻してから shift_jis でデコード
const fromPercentSjis = (s) => {
  const bytes = new Uint8Array(
    s.match(/%[0-9A-F]{2}/gi).map((h) => parseInt(h.slice(1), 16))
  );
  return new TextDecoder("shift_jis").decode(bytes);
};

fromPercentSjis("%82%B1%82%F1%82%C9%82%BF%82%CD");
// → "こんにちは"

ラベルshift_jis / euc-jp / iso-2022-jp はEncoding Standardの命名に従います。fatal: trueオプションをつけると、壊れたバイト列に対して例外を投げて検知できます。

5. 実例:メルマガ解除URL

想定シナリオ:「https://example.jp/unsubscribe?name=%97L%8C%F8
手軽屋のURLデコードでは「−Lè−Hø」のような壊れた文字に。Shift_JISで復号すると「有効」と読めます。%97L = 「有」(0x97 0x4C)、%8C%F8 = 「効」(0x8C 0xF8)。

この種のURLは2005年〜2010年頃のCMS(MovableType・XOOPS・古いWordPressのpermalink)で頻出。記事の永続URLには現在もShift_JIS時代のものが残っていることがあります。

6. 復元できないケース・誤検知

7. 「文字化けしない」URLを作る側のベストプラクティス

8. 関連ツール・記事

URLデコードで文字を復元する