🙆

【Python】NFD の濁音・半濁音を NFC に変換する

2024/06/17に公開

Unicode 正規化には unicodedata.normalize を使う

python3

>>> import unicodedata
>>> str = 'か\u3099'
>>> len(str)
2
>>> ret = unicodedata.normalize('NFC', str)
>>> len(ret)
1
>>> str2 = 'が'
>>> len(str2)
1
>>> ret2 = unicodedata.normalize('NFD', str2)
>>> len(ret2)
2

Unicode 正規化を行う場合、無関係な文字を巻き込まないようにする必要がある。次のコードは旧字体が新字体に置き換わってしまう例である。

>>> unicodedata.normalize('NFC', '神')
'神'
>>> unicodedata.normalize('NFD', '神')
'神'

絞り込みのために regex を導入する

python -m pip install regex

まずは濁音・半濁音をマッチさせる

>>> import regex
>>> pattern = regex.compile(r'[\p{Script=Hiragana}\p{Script=Katakana}][\u3099\u309A]')
>>> pattern.findall('ハ\u309Aハ\u309Aと神')
['パ', 'パ']

書記素クラスターにマッチさせるには \X を使う

>>> pattern = regex.compile(r'\X')
>>> pattern.findall('ハ\u309Aハ\u309A')
['パ', 'パ']

リスト内包式で展開してみる

>>> str = 'ハ\u309Aハ\u309Aと神'
>>> iter = regex.compile(r'\X').finditer(str)
>>> [m.group() for m in iter]
['パ', 'パ', 'と', '神']

処理をまとめる。ラムダ式でかなと濁点・半濁点の文字を検出する処理とそれらを限定して Unicode 正規化する関数を定義する

>>> is_nfd_dakuon = lambda str: regex.compile(r'[\p{Script=Hiragana}\p{Script=Katakana}][\u3099\u309A]').match(str)
>>> dakuon_normalize = lambda str: unicodedata.normalize('NFC', str) if is_nfd_dakuon(str) else str

テストの文字列を用意する。書記素単位で取り出すのにリスト内包式を使う

>>> str = 'ハ\u309Aハ\u309Aと神'
>>> iter = regex.compile(r'\X').finditer(str)
>>> [m.group() for m in iter]
['パ', 'パ', 'と', '神']

コードポイントの数も数える

>>> iter = regex.compile(r'\X').finditer(str)
>>> [len(m.group()) for m in iter]
[2, 2, 1, 1]

最後に実際に Unicode 正規化を実施する

>>> [dakuon_normalize(m.group()) for m in iter]
['パ', 'パ', 'と', '神']
>>> [len(dakuon_normalize(m.group())) for m in iter]
[1, 1, 1, 1]

Discussion