全角と半角の変換

最近、全角と半角の変換処理を行なったことがあります。

やり方としては、key を Unicode コードポイント (ord 関数によって)、value を置換後の文字列を持つの辞書を作成して、変換テーブルとして str.translate メソッドに渡すだけです。

注意すべきなのは、半角から全角に変換する時の濁点の処理です。

たとえば、半角のガは「カ」と濁点「゙」の 2 文字で構成されています。 str.translate は特定の 1 文字を別の文字列に変換するメソッドなので、半角から全角に変換する時は、濁音を(str.replace メソッドとかで) 先に処理する必要があります。

ちょっと調べてみると、実は unicodedata という Python 標準ライブラリがあります。

半角カタカナから全角カタカナへの変換は、 unicodedata.normalizeを使って、正規形を NFKC にすれば簡単に変換できます。

こういう手法は Unicode正規化 と呼ばれています。 Unicode 正規化は、Unicodeの等価性 というものに基づいて定義されています。 Unicode の等価性には正準等価(Canonical Equivalent)と互換等価(Compatibility Equivalent)、この2種類があります。 この2種類に対して、Unicodeはさらに合成か分解かの正規形(CとD)を定義しています。 したがって、Unicodeの正規化形式は ‘NFC’、’NFD’、’NFKC’、’NFKD’ の4つの種類があります。

In [1]: import unicodedata

In [2]: hoge = 'フシギダネ㈱㍍â'

In [3]: fuga = unicodedata.normalize('NFC', hoge)

In [4]: print(fuga)
フシギダネ㈱㍍â

In [5]: print(list(map(ord, fuga)))
[65420, 65404, 65399, 65438, 65408, 65438, 65416, 12849, 13133, 226]

In [6]: fuga = unicodedata.normalize('NFD', hoge)

In [7]: print(fuga)
フシギダネ㈱㍍â

In [8]: print(list(map(ord, fuga)))
[65420, 65404, 65399, 65438, 65408, 65438, 65416, 12849, 13133, 97, 770]

In [9]: fuga = unicodedata.normalize('NFKC', hoge)

In [10]: print(fuga)
フシギダネ()メートルâ

In [11]: print(list(map(ord, fuga)))
[12501, 12471, 12462, 12480, 12493, 40, 26666, 41, 12513, 12540, 12488, 12523, 226]

In [12]: fuga = unicodedata.normalize('NFKD', hoge)

In [13]: print(fuga)
フシキ()メートルâ

In [14]: print(list(map(ord, fuga)))
[12501, 12471, 12461, 12441, 12479, 12441, 12493, 40, 26666, 41, 12513, 12540, 12488, 12523, 97, 770]

見た目は同じですが、Unicodeでは異なっています。

たとえば、NFKC では hex(12462)=0x30ae 全角の「ギ」になりますが、NFKD では hex(12461)=0x30ad 全角の「キ」と hex(12441)=0x3099 という濁点に分解されます。


それにしても、半角カタカナってあまり使われないですね(顔文字以外はほぼ見たことない気がします…)。