とは
- 「ア」と「ア」, 「①」と「1」など等価な文字の表記を統一する操作. NLPで前処理としてよく使われる
- "Unicode正規化(ユニコードせいきか、英語: Unicode normalization)とは、等価な文字や文字の並びを統一的な内部表現に変換することでテキストの比較を容易にする、テキスト正規化処理の一種である。" - Wikipedia
NFD, NFC, NFKD, NFKC
4種類の正規化形式が存在する.
「NFD (Normalization Form Canonical Decomposition)」
「NFC (Normalization Form Canonical Composition)」
「NFKD (Normalization Form Compatibility Decomposition)」
「NFKC (Normalization Form Compatibility Composition)」
- NFDとNFCは正準等価性 (Canonical Equivalent) によって分解. 同一文字とみなす条件がより厳しい.
- NFKDとNFKCは互換等価性 (Compatibility Equivalent) によって分解. 同一文字とみなす条件がより緩い.
- NFDの後, 再度合成するのがNFC
- NFKDの後, 再度合成するのがNFKC
例1. 正準等価性と互換等価性
$ python
>>> import unicodedata
>>> text = "①11"
>>> unicodedata.normalize("NFD",text)
'①11'
>>> unicodedata.normalize("NFC",text)
'①11'
>>> unicodedata.normalize("NFKD",text)
'111'
>>> unicodedata.normalize("NFKC",text)
'111'
バイト列で見てみると
>>> text.encode("utf-8")
b'\xe2\x91\xa01\xef\xbc\x91'
>>> unicodedata.normalize("NFD",text).encode("utf-8")
b'\xe2\x91\xa01\xef\xbc\x91'
>>> unicodedata.normalize("NFC",text).encode("utf-8")
b'\xe2\x91\xa01\xef\xbc\x91'
>>> unicodedata.normalize("NFKD",text).encode("utf-8")
b'111'
>>> unicodedata.normalize("NFKC",text).encode("utf-8")
b'111'
- 正準等価性の観点では, 「①」と「1」と「1」は同一視しない
- 互換等価性の観点では全て同一視
例2. 分解と合成
>>> text="ガガが"
>>> unicodedata.normalize("NFD",text)
'ガガが'
>>> unicodedata.normalize("NFC",text)
'ガガが'
>>> unicodedata.normalize("NFKD",text)
'ガガが'
>>> unicodedata.normalize("NFKC",text)
'ガガが'
- 半角「ガ」と全角「ガ」は互換等価であるらしい. ひらがなとカタカナは区別される.
- NFDとNFC, NFKDとNFKCが全く同じように見えるが, 実は
>>> text.encode("utf-8")
b'\xef\xbd\xb6\xef\xbe\x9e\xe3\x82\xac\xe3\x81\x8c'
>>> unicodedata.normalize("NFD",text).encode("utf-8")
b'\xef\xbd\xb6\xef\xbe\x9e\xe3\x82\xab\xe3\x82\x99\xe3\x81\x8b\xe3\x82\x99'
>>> unicodedata.normalize("NFC",text).encode("utf-8")
b'\xef\xbd\xb6\xef\xbe\x9e\xe3\x82\xac\xe3\x81\x8c'
>>> unicodedata.normalize("NFKD",text).encode("utf-8")
b'\xe3\x82\xab\xe3\x82\x99\xe3\x82\xab\xe3\x82\x99\xe3\x81\x8b\xe3\x82\x99'
>>> unicodedata.normalize("NFKC",text).encode("utf-8")
b'\xe3\x82\xac\xe3\x82\xac\xe3\x81\x8c'
コンピュータにはこのように見えている. NFDとNFKDのバイト列が他より長いことが分かる.
-
正規化前の文字は「ガ=\xef\xbd\xb6\xef\xbe\x9e」「ガ=\xe3\x82\xac」「が=\xe3\x81\x8c」このようにそれぞれ対応している. つまり
- NFDでは「ガ(\xe3\x82\xac)」を「カ(\xe3\x82\xab)+゙ (\xe3\x82\x99)」に, 「が(\xe3\x81\x8c)」を「か(\xe3\x81\x8b)+゙ (\xe3\x82\x99)」に分解した.
- NFCではこのように分解した結合文字列を再合成して, 元のバイト列に戻した.
- NFKDではNFDの動作に加え, 「ガ」を「カ(\xe3\x82\xab)+゙ (\xe3\x82\x99)」に分解している.
- NFKCではそれを再合成.
- NFDでは「ガ(\xe3\x82\xac)」を「カ(\xe3\x82\xab)+゙ (\xe3\x82\x99)」に, 「が(\xe3\x81\x8c)」を「か(\xe3\x81\x8b)+゙ (\xe3\x82\x99)」に分解した.
-
Unicodeには見た目上同じ文字(「が」)でも、「が(\xe3\x81\x8c)」「か(\xe3\x81\x8b)+゙ (\xe3\x82\x99)」のように2通りの方法で表せるものがある. 前者は「合成済み文字」, 後者は「結合文字列 (基底文字+結合文字の組み合わせ)」と呼ばれる.
-
つまりUnicode正規化の「分解 (Decomposition)」は合成済み文字を結合文字列にする操作, 「合成 (Composition)」は基底文字と結合文字の組み合わせによる結合文字列を合成済み文字にする操作である.
-
どれを使うかは場面に応じる必要があるが, NFKCが最もよく使われている印象.