前回の記事でユニコード正規化を紹介し、NFD/NFC/NFKD/NFKCの4種類の形式があるという話をしました。
今回はそれぞれの形式で正規化した時の振る舞いを見ていこうと思います。
元々、各形式の厳密な定義の話をしようと結構前から調べていたのですが、
正準等価の方(NFDとNFC)がまだ自分の中で腑に落ちてないので、今回は色々動かして結果を眺めることにします。
互換等価の方は、意味は同じで見た目が違う文字、というざっくりとした理解で大丈夫のようです。(たぶん)
ちなみに、4つの中でどれを使えば良いか迷ってるだけ、という人は NFKC を使えば大丈夫だと思います。
それでは、どの型で正規化されるかによって結果が変わる文字をいくつか取り上げて挙動を見てみましょう。
一つ目はひらがなの「が」です。
NFC/NFKCでは特に変化がなく、 NFD/NFKDでも、見た目は変化してないのですが、文字コードに直すと「か」と「濁点」に分解されていることがわかります。
import unicodedata
forms = ["NFC", "NFD", "NFKC", "NFKD"]
text = "が"
print("原型:", text, " 文字コード:", text.encode("utf-8"))
for form in forms:
print(
form,
":",
unicodedata.normalize(form, text),
" 文字コード:",
unicodedata.normalize(form, text).encode("utf-8")
)
# 以下出力
'''
原型: が 文字コード: b'\xe3\x81\x8c'
NFC : が 文字コード: b'\xe3\x81\x8c'
NFD : が 文字コード: b'\xe3\x81\x8b\xe3\x82\x99'
NFKC : が 文字コード: b'\xe3\x81\x8c'
NFKD : が 文字コード: b'\xe3\x81\x8b\xe3\x82\x99'
'''
つぎは半角カタカナの「カ」です。
NFKC/NFKDでは全角のカタカナに正規化してくれていることがわかります。
NFC/NFDは変化なしです。
text = "カ"
print("原型:", text, " 文字コード:", text.encode("utf-8"))
for form in forms:
print(
form,
":",
unicodedata.normalize(form, text),
" 文字コード:",
unicodedata.normalize(form, text).encode("utf-8")
)
# 以下出力
'''
原型: カ 文字コード: b'\xef\xbd\xb6'
NFC : カ 文字コード: b'\xef\xbd\xb6'
NFD : カ 文字コード: b'\xef\xbd\xb6'
NFKC : カ 文字コード: b'\xe3\x82\xab'
NFKD : カ 文字コード: b'\xe3\x82\xab'
'''
次は、「ガ」です。
NFC/NFDは変化しないのは「カ」の時と同じですが、
NFKCとNFKDで、文字コードが違います。NFKDの方は「カ」と「濁点」に分解されたままですが、
NHKCではそれが結合されています。
text = "ガ"
print("原型:", text, " 文字コード:", text.encode("utf-8"))
for form in forms:
print(
form,
":",
unicodedata.normalize(form, text),
" 文字コード:",
unicodedata.normalize(form, text).encode("utf-8")
)
# 以下出力
'''
原型: ガ 文字コード: b'\xef\xbd\xb6\xef\xbe\x9e'
NFC : ガ 文字コード: b'\xef\xbd\xb6\xef\xbe\x9e'
NFD : ガ 文字コード: b'\xef\xbd\xb6\xef\xbe\x9e'
NFKC : ガ 文字コード: b'\xe3\x82\xac'
NFKD : ガ 文字コード: b'\xe3\x82\xab\xe3\x82\x99'
'''
互換等価性は正準等価性より広い概念で、正準等価であるものは何であれ互換等価とのこと(参考:wikipedia – Unicodeの等価性)
なので、NFKCやNFKDが元の文字列と同じで、NFCやNFDは元の文字列と異なる、という例はおそらく無いのでしょう。
このほか特殊記号など色々試してみましたが、全体的に NFKC が僕が欲しい結果になることが多かったので、
普段はこれを利用しています。