テキスト値の三層
人が見るのは文字の並びで、ときには前の文字と結び付く記号も含まれます。Unicode は抽象的な文字にコードポイントを割り当て、UTF-8 などの符号化方式がそのコードポイントを保存・送信用のバイト列に変えます。目に見える1文字が複数のコードポイントから成ることも、1つのコードポイントが UTF-8 では複数バイトになることもあります。したがって、バイト数を数えても文字数や利用者が1文字と認識する単位の数とは限りません。text-to-code-points ツールは文字列の Unicode 値を、text-to-UTF-8-bytes ツールは別の層であるバイト列を示します。これらは不一致の診断に使うもので、受け手の文字コードを考えずに置き換えられる別表記ではありません。
café を表す二つの文字列
目に見える単語 café は、末尾に単一のコードポイント U+00E9(LATIN SMALL LETTER E WITH ACUTE)を含められます。これが合成形です。一方、U+0065(LATIN SMALL LETTER E)の後に U+0301(COMBINING ACUTE ACCENT)を置くこともできます。後者はコードポイントが1つ多いものの、表示上は同じ単語に見えることがよくあります。UTF-8 では、合成形の単語の16進バイト列は 63 61 66 C3 A9、分解形では 63 61 66 65 CC 81 です。バイト列が異なるため、バイト単位の比較やハッシュ値も異なり得ます。この例では NFC が合成形に、NFD が分解形に対応します。埋め込み変換ツールは、合成形を 99 97 102 195 169、分解形を 99 97 102 101 204 129 と10進数のバイト値で示します。これらを上の16進表記と比べてください。
- café:末尾は U+00E9 → C3 A9
- café:末尾は U+0065 U+0301 → 65 CC 81
- NFC → 合成形 · NFD → 分解形
正規化の効果と効果のないこと
Unicode 正規化には、正準的に等しい文字列の表現をそろえるための規則があります。NFC は分解後に合成し、NFD は分解した並びを保ちます。異なるキーボード、ファイル、システムから来たテキストを比較するときに役立ちます。ただし、単語を翻訳したりアクセントを取り除いたり、見た目の似た文字列をすべて同じにしたりはしません。別の文字体系に属する似た字形には異なる意味がある場合もあります。互換性正規化の NFKC と NFKD はさらに広い変更を行い、表記上の区別が失われる可能性があります。その変更が許される用途でだけ選んでください。比較、索引付け、ハッシュ化の各段階で同じ正規化規則を使わなければ、見た目が同じ入力も異なる結果になり得ます。
順序を決めてバイト列を調べる
名前が一致しないときは、まず元の入力を保存します。コードポイントを調べ、アクセントが合成済みの一文字なのか、基底文字と結合記号なのかを確かめてください。API を通るときやファイルに書き込むときの表現は UTF-8 のバイト列でも調べます。コピーを NFC または NFD に正規化し、コードポイントを比較します。その後、逆向きの UTF-8 バイト変換器でバイト列が有効であり、意図した文字に復号されるか確かめてください。ファイルを交換するときは、どの文字コードを使うか明記します。文字化けや置換文字は復号時の問題を示すことが多く、誤って復号した後の文字列を正規化しても失われた元のバイト列は復元できません。
目的に合った等価性を選ぶ
ファイルの厳密な整合性確認、ハッシュ、署名ではバイト列が決定的です。正規化を変えればバイト列が変わり、ダイジェストも変わります。利用者向けの検索や重複判定では、正規化を先に行う設計もありますが、その規則を説明し、テストすべきです。大文字と小文字、句読点、空白、言語ごとの照合順序は別の問題で、NFC だけでは解決しません。必要な処理として定められていない限り、表示や書き出しの際に利用者の綴りを黙って変更しないでください。下のツールでは、café から始めて UTF-8 のバイト列を調べ、次に e と結合アクセントを使って入力した版をコードポイントと正規化のツールに貼り付けます。見た目には小さな違いがデータ処理のどこで生じるかが分かります。