Unicode 正規化変換 (NFC / NFD / NFKC / NFKD) | 4 形式並列ビューア
NFC / NFD / NFKC / NFKD の 4 形式を 1 画面に並べ、形式ごとの文字数と UTF-8 バイト数、原文との差分を一括で確認できる正規化ビューアです。コードポイント単位の挙動を視覚的に追えるので、ファイル名照合や検索 index のキーずれを切り分けたい開発者の調査用途を想定しています。
💡 このツールについて
「macOS で作ったフォルダを Linux に持っていったら同じファイル名なのに別ファイルとして並んでいる」「DB の WHERE name = ? で見つからないユーザー名がある」「OAuth の sub 比較で同じはずの username が一致しない」。こういった一見「文字化けではないのに等値判定されない」バグの 9 割は Unicode 正規化形式のずれが原因です。
このツールは入力テキストを NFC / NFD / NFKC / NFKD の 4 形式すべてに同時変換し、それぞれの結果テキスト・コードポイント数 (String.prototype.normalize ベース)・UTF-8 にエンコードしたときのバイト数・原文からのバイト増減を 1 画面に並べます。「入力の形式」欄では原文がどの形式に該当しているかも判定するので、相手システムが期待する形式と現在の形式の差分を最短でつかめます。
特に NFKC / NFKD は互換等価分解を行うため、全角英数字 → 半角、半角カナ → 全角カナ、fi (合字) → fi のような「見た目が変わってもよい」変換が走ります。検索や名前ハッシュ前の正規化キーとして妥当か、データを犠牲にしていないかをその場で目視確認できます。
🧐 よくある質問
Q. NFC と NFD はどちらを使うべきですか? A. アプリケーションが他システムと交換するテキストはほぼ全例で NFC を推奨します。W3C は HTML / URL / DOM 文字列を NFC に揃えるよう Charmod-Norm で勧告しています。NFD は内部処理 (検索インデックスのアクセント除去や grapheme 単位の操作) で一時的に使う中間表現と理解しておくと混乱しません。
Q. macOS の HFS+ は NFD で保存すると聞きました。今もそうですか? A. HFS+ は NFD ベースの独自正規化 (NFD with reserved range) を採用していましたが、現行 APFS では正規化を行わずバイト列そのままを保持する仕様に変わりました。ただし古いアーカイブ・古い Time Machine バックアップ・rsync 経由の旧 HFS+ 由来データは NFD のまま流通しています。「macOS = NFD」と決め打ちせず、現物を本ツールで確認してください。
Q. NFKC と NFKD はどんな場面で使いますか?
A. 検索や照合のキー正規化、いわゆる「正規化キー」生成に使います。Python の str.casefold() 相当の処理の前段に NFKC を噛ませて全角・半角・合字を吸収する、というパターンが代表例です。ただし NFKC は ① → 1 のように意味を変える変換も行うので、保存テキスト本体には適用しないのが定石です。
Q. 結合文字 (combining mark) と独立文字の違いは?
A. たとえば é は単一コードポイント U+00E9 (NFC) としても、e (U+0065) + 結合アキュート (U+0301) (NFD) としても表現できます。前者は 1 文字、後者は 2 文字としてカウントされ、UTF-8 では前者が 2 バイト、後者が 3 バイトです。表示は同じでも === 比較や LENGTH() で別物になるのが「正規化問題」の典型像です。
Q. JSON や HTTP ヘッダで送るときに気をつけることは? A. JSON の文字列は仕様上はバイト列としてしか規定されていませんが、相互運用上 NFC で揃えるのが安全です。HTTP ヘッダや URL も NFC が事実上のデファクトで、IDN ホスト名は IDNA で別途正規化されるため Unicode 正規化とは独立に扱う必要があります。
Q. JS の normalize() で対応していない形式はありますか?
A. ECMAScript 2015+ の String.prototype.normalize は NFC / NFD / NFKC / NFKD の 4 形式すべてに対応しています。実装は ICU 由来で、本ツールが使用しているのもこれです。CJK 互換漢字 (U+F900 ブロック) の NFC → 統合漢字置換、ハングル合字の合成・分解、結合マークの canonical ordering も仕様準拠で行われます。
Q. 大量のテキストでも動きますか? A. ブラウザ内処理で完結するため、数 MB 程度のテキストでも一瞬で 4 形式が並びます。ただしブラウザの textarea は数万文字を超えると編集レスポンスが落ちるため、巨大ログの調査には先頭数千文字をサンプリングするほうが現実的です。
📚 豆知識
Unicode 正規化が「同じに見える文字を 1 つに揃える」だけの仕組みに見えて、実は「同じに見えるが意味は別」を残す設計になっている、というのはあまり知られていない論点です。たとえばギリシャ文字の Ω (U+03A9, GREEK CAPITAL LETTER OMEGA) と数学記号の Ω (U+2126, OHM SIGN) は NFC で前者に統一されますが、ローマ数字の Ⅻ (U+216B) と XII は NFKC でも別物のまま残ります。「ohm 記号は文字としては Ω でいい」が「ローマ数字は数字としての意味があるからローマ数字に潰してはいけない」という、見た目同一性とセマンティック同一性の線引きが Unicode コンソーシアムによって個別判断されているわけです。
もう一つ、日本語環境で実害が出やすいのが「合成済み濁点・半濁点」と「結合濁点・半濁点」の差です。が は単独コードポイント U+304C で書ける一方、か (U+304B) + 結合濁点 (U+3099) でも書けます。Mac の旧 HFS+ 流通テキストや一部の OCR 出力は後者を出力するため、grep が で引っかからない、フォーム入力のバリデーションがすり抜ける、といった現象が起きます。本ツールで NFC バイト数と NFD バイト数の差を見ると、どちらの形式が混ざっているかを 1 画面で切り分けられます。