本記事はアフィリエイトリンクを含みます。Amazon のアソシエイトとして、TechAthletes は適格販売により収入を得ています。価格・割引・在庫は変動するため、最新の条件は各公式サイトでご確認ください。
見た目が同じ文字列をどうそろえるか
PythonでUnicode文字列を扱うとき、見た目が同じであることと、同じ表現であることは分けて考える必要があります。公式ドキュメントは、U+00C7(LATIN CAPITAL LETTER C WITH CEDILLA)という文字を、U+0043(LATIN CAPITAL LETTER C)とU+0327(COMBINING CEDILLA)の並びでも表現できると説明しています。
ここで必要になるのが正規化です。unicodedata.normalize(form, unistr)には、NFC、NFD、NFKC、NFKDという4つの形式を指定できます。ただし、どれも同じ変換をするわけではありません。形式を選ぶには、「どの種類の分解をするか」と「分解した後に合成するか」という軸で整理すると、公式の説明を読み分けられます。
また、正規化と、利用者が一文字と感じる単位への分割も別の処理です。今回の問いは、文字列をどの正規化形式にそろえるかです。一文字の区切りを決める機能と混同せず、まず4形式の変換内容を比較します。
公式が説明する分解と合成
Pythonの公式ドキュメントは、NFDについて次のように記しています。
Normal form D (NFD) is also known as canonical decomposition, and translates each character into its decomposed form.
出典: https://docs.python.org/3/library/unicodedata.html (取得日 2026-10-16)
NFDは正準分解を行う形式です。一方、NFCは最初に正準分解を行い、その後で合成済みの文字へ再び合成します。両者を分けるのは、分解後の合成があるかどうかです。
文字の分解マッピングを調べる関数として、同じ資料にはdecompositionも掲載されています。公式の入力例を、そのまま引用します。
>>> unicodedata . decomposition ( 'Ã' )
これは文字に割り当てられた分解マッピングを取得する例です。decompositionはマッピングを文字列として返す関数であり、文字列全体を指定形式に正規化するnormalizeとは役割が異なります。
残るNFKDとNFKCは、互換等価性に基づく形式です。公式は、U+2160(ROMAN NUMERAL ONE)とU+0049(LATIN CAPITAL LETTER I)を例に挙げています。前者は既存の文字集合との互換性のためにUnicodeでサポートされている、と説明されています。
NFKDは互換分解を行い、互換文字を対応する文字へ置き換えます。NFKCは互換分解を行った後に、正準合成を行います。つまり、名前に含まれるKの有無だけでなく、最後がCかDかも処理内容を左右します。
4形式を分解の種類と合成の有無で比べる
公式の説明を、同じ項目で並べると次のようになります。
| 形式 | 分解の種類 | 分解後の合成 |
|---|---|---|
| NFC | 正準分解 | 行う |
| NFD | 正準分解 | 行わない |
| NFKC | 互換分解 | 正準合成を行う |
| NFKD | 互換分解 | 行わない |
この表では、NFCとNFDが同じ正準分解の組、NFKCとNFKDが同じ互換分解の組になります。NFCとNFKCを比較すると、どちらも分解後に合成しますが、その前に行う分解が異なります。NFDとNFKDを比較すると、どちらも分解する形式ですが、互換文字の置き換えを含むかどうかが異なります。
したがって、「合成された形にしたい」という条件だけでは、NFCとNFKCのどちらかは決まりません。同様に、「分解した形にしたい」という条件だけでは、NFDとNFKDのどちらかは決まりません。互換等価性に基づく置き換えを処理の目的に含めるか、という判断がもう一つ必要です。
形式の選び方と「正規化済み」の落とし穴
正準分解を使い、さらに合成した形式を必要とするならNFC、分解した形式を必要とするならNFDが対応します。互換文字の置き換えを含める場合は、合成まで行うNFKCと、分解するNFKDから選ぶことになります。どれかを一律に選ぶのではなく、必要な変換を表の二つの軸に当てはめて選びます。
指定形式になっているかを確認する関数は、unicodedata.is_normalized(form, unistr)です。こちらにも同じ4形式を指定できます。「正規化済みか」という確認には、どの形式について確認しているかという条件が伴います。
公式ドキュメントには、二つの文字列が正規化済みで、人間には同じに見えても、一方に結合文字があり、もう一方にない場合には、等しいと比較されないことがあるという注意もあります。「両方とも正規化した」という説明だけで比較の条件を済ませず、指定した形式まで確認する必要があります。
さらに、正規化を一文字の区切りの判定として扱うこともできません。Unicodeの文書は、利用者が一文字と考える単位が、複数のUnicodeコードポイントからなる場合を説明しています。書記素クラスタは、そのような単位をプログラムで扱うための近似です。
出典: https://www.unicode.org/reports/tr29/ (取得日 2026-10-16)
Pythonの資料では、書記素クラスタを反復する機能はiter_graphemesとして別に掲載されています。正規化形式を選ぶ作業と、文字列をどの単位で区切るかを選ぶ作業は、目的を分けて扱うのがよいでしょう。
自分の環境で確かめる手順
次のコマンドは、公式が例示する文字を使って4形式を確認するための手順です。pythonで対象のPython環境を起動できる場所で実行してください。ここでは実行結果は掲載しません。
python - <<'PY'
import unicodedata as u
for form in ("NFC", "NFD", "NFKC", "NFKD"):
print(form, ascii(u.normalize(form, "\u00C7")), ascii(u.normalize(form, "\u0043\u0327")), ascii(u.normalize(form, "\u2160")))
PY
確認するときは、まずNFCとNFD、次にNFKCとNFKDを見比べ、合成の有無による違いを調べます。その後でNFCとNFKC、NFDとNFKDを比較し、分解の種類による違いを調べてください。見た目だけで判断せず、指定した形式と文字列の表現を対応させて読むことが、この確認の目的です。
文字コードを仕組みから押さえたいとき
「1文字」が符号位置いくつ分になるかを規格の側から押さえたいときは、『[改訂新版]プログラマのための文字コード技術入門』(技術評論社)が使えます。技術評論社の書籍ページ(取得日 2026-10-21)の目次には「サロゲートペア」と「結合文字 1文字が1符号位置ではない」の節があります。例は Java と Ruby で、Python や JavaScript の文字数の数え方は扱っていません。

コメント