Python入門トップページ


目次

  1. プログラミング言語
  2. Anaconda - Jupyter Notebook / JupyterLab の環境設定
  3. Python の基礎
  4. リスト,タプル,辞書,集合
  5. 再び Jupyter Notebook の操作
  6. Python の制御構文
  7. 関数
  8. 便利な関数など
  9. リストの内包表記
  10. 多次元リスト
  11. クラス
  12. 演習問題
  13. 雑多な情報

Python の基礎

雑多な情報

全角文字・半角文字の変換と文字列の正規化

文章に対して自然言語処理による分析や文字列の検索を行う前に,正規化によって全角文字と半角文字の表記ゆれを統一する必要がある場合があります.ここでは,全角アルファベットや全角数字,全角記号を半角文字に変換する方法を説明します.また,正規化の方法は複数あるので,その特徴についても考察します.

目次に戻る

日本語の文章を正規化する

まず,unicodedata ライブラリをプログラムの先頭でインポートします.

import unicodedata

ここでは全角文字と半角文字で表記の揺れが存在する文字列を準備します.なお,わかりやすいように,全角のアルファベット,数字,記号はハイライトしています.カッコや空白についても全角と半角が混在していることにも注意してください.

msg = "近年日本で開催されたサミット(主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima Summit です。"
print(msg)
近年日本で開催されたサミット主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima ummit です。

上の文字列を正規化して半角に変換します.結論から述べると,次のように unicodedata.normalize() 関数に 'NFKC' という正規形を指定するとうまくいきます.

normalized_msg = unicodedata.normalize('NFKC', msg)
print(normalized_msg)
近年日本で開催されたサミット(主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima Summit です。

上では,unicodedata.normalize() 関数の第1引数に 'NFKC' を指定しました.これ以外にも 'NFC''NFD''NFKD' が指定できます.次のセクションではこれらの違いについて考察します.

目次に戻る

正規化の種類

上と同じ文章を正規化するときの第1引数を変化させてみます.なお,unicodedata.normalize() の詳細はこちらを参照してください.

まず,'NFC' を指定します.この場合は表記の揺れが統一されませんでした.

normalized_msg = unicodedata.normalize('NFC', msg)
print(normalized_msg)
近年日本で開催されたサミット主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima ummit です。

次に 'NFKC' です.これはすでに示したとおり,半角に統一されています.

normalized_msg = unicodedata.normalize('NFKC', msg)
print(normalized_msg)
近年日本で開催されたサミット(主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima Summit です。

'NFD' では 'NFC' と同じ結果で,表記の揺れは統一されません.

normalized_msg = unicodedata.normalize('NFD', msg)
print(normalized_msg)
近年日本で開催されたサミット主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima ummit です。

'NFKD' では 'NFKC' と同じ結果で,半角文字に統一されました.

normalized_msg = unicodedata.normalize('NFKD', msg)
print(normalized_msg)
近年日本で開催されたサミット(主要国会議)は G8 Hokkaido TOYAKO Summit、G7 JAPAN 2016 Ise-Shima Summit と、G7 Hiroshima Summit です。

つまり,上の文字列を半角文字に変換するには 'NFKC' または 'NFKD' を指定すれば良いことになります.(すでに説明したとおり,通常は 'NFKC' でよいはずですが)これらの違いについて次のセクションで更に分析してみます.具体的には「ヴ」などの文字の取り扱いで違いが出てくることになります.またドイツ語のウムラウトなどの取り扱いでも違いが現れます.

目次に戻る

分析と考察

ここでは unicodedata.normalize() 関数で指定する4種類の正規形('NFC''NFKC''NFD''NFKD')について分析しその結果を考察してみます.なお,これらの詳細は docs.python.orgWikipedia で確認できます.(私はこのあたりの理論については詳しくありません.)

まず文字列を一文字ごと文字コードに変換して表示する関数を準備します.

# 文字列を1文字ずつ文字コードにして表示する
def print_ord(s):
    print(s, ": ", end='')
    for c in s:
        print(hex(ord(c)), end=' ')
    print(' ')

次に,分析したい文字の組み合わせを s1s2 にセットして,その文字コードを確認します.分析したい組み合わせのコメントを解除していろいろ試してください.

s1 = '1'  # 半角文字
s2 = '1'  # 全角文字

# s1 = 'A'  # 半角文字
# s2 = 'A'  # 全角文字

# s1 = 'ア'  # 半角文字
# s2 = 'ア'  # 全角文字

# s1 = '('  # 半角カッコ
# s2 = '('  # 全角カッコ

# s1 = ' '  # 半角空白
# s2 = ' '  # 全角空白

# s1 = 'km'  # 半角文字
# s2 = '㎞'  # 全角文字(特殊文字)

# s1 = 'ヘクタール'  # 半角文字
# s2 = '㌶'  # 全角文字(特殊文字)

# s1 = 'ヴ'   # 「ヴ」という一文字
# s2 = 'ヴ'   # 「ウ」と「  ゙」 の合字

# s1 = 'fi'
# s2 = 'fi'  # 合字

# s1 = 'u'
# s2 = 'ü' # ドイツ語のウムラウト u

# s1 = 'a'
# s2 = 'å'  # 上リング付き a (ノルウェー語,デンマーク語,スウェーデン語で用いられる)

print_ord(s1)
print_ord(s2)
1 : 0x31
1 : 0xff11

正規化の方法に 'NFC''NFKC''NFD''NFKD' を使って結果を確認します.

normalized_s1 = unicodedata.normalize('NFC', s1)
normalized_s2 = unicodedata.normalize('NFC', s2)
print_ord(normalized_s1)
print_ord(normalized_s2)
1 : 0x31
1 : 0xff11
normalized_s1 = unicodedata.normalize('NFKC', s1)
normalized_s2 = unicodedata.normalize('NFKC', s2)
print_ord(normalized_s1)
print_ord(normalized_s2)
1 : 0x31
1 : 0x31
normalized_s1 = unicodedata.normalize('NFD', s1)
normalized_s2 = unicodedata.normalize('NFD', s2)
print_ord(normalized_s1)
print_ord(normalized_s2)
1 : 0x31
1 : 0xff11
normalized_s1 = unicodedata.normalize('NFKD', s1)
normalized_s2 = unicodedata.normalize('NFKD', s2)
print_ord(normalized_s1)
print_ord(normalized_s2)
1 : 0x31
1 : 0x31

「1」と「1」,「A」と「A」,「ア」と「ア」,「(」と「(」,「半角空白」と「全角空白」について上の5つのサンプルコードを実行した結果をまとめると次のようになりました.この表を見る限り,全角文字と半角文字の表記の揺れを統一するだけであれば,'NFKC' でも 'NFKD' でも良さそうです.具体的には,アルファベット,数字,記号は半角に統一され,カナは全角に統一されます.

文字 処理なし 'NFC' 'NFKC' 'NFD' 'NFKD'
1 0x31 0x31 0x31 0x31 0x31
0xff11 0xff11 0x31 0xff11 0x31
A 0x41 0x41 0x41 0x41 0x41
0xff21 0xff21 0x41 0xff21 0x41
0xff71 0xff71 0x30a2 0xff71 0x30a2
0x30a2 0x30a2 0x30a2 0x30a2 0x30a2
( 0x28 0x28 0x28 0x28 0x28
0xff08 0xff08 0x28 0xff08 0x28
(半角空白) 0x20 0x20 0x20 0x20 0x20
 (全角空白) 0x3000 0x3000 0x20 0x3000 0x20

次は「km」という2文字と「㎞」という1文字がどのように正規化されるか,「ヘクタール」という5文字と「㌶」という1文字がどのように正規化されるかを分析した結果を表に示します.この結果,'NFKC''NFKD' では「km」(2文字)や「ヘクタール」に変換されることがわかります.

文字(列) 処理なし 'NFC' 'NFKC' 'NFD' 'NFKD'
km 0x6b 0x6d 0x6b 0x6d 0x6b 0x6d 0x6b 0x6d 0x6b 0x6d
0x339e 0x339e 0x6b 0x6d 0x339e 0x6b 0x6d
ヘクタール 0x30d8 0x30af 0x30bf 0x30fc 0x30eb 0x30d8 0x30af 0x30bf 0x30fc 0x30eb 0x30d8 0x30af 0x30bf 0x30fc 0x30eb 0x30d8 0x30af 0x30bf 0x30fc 0x30eb 0x30d8 0x30af 0x30bf 0x30fc 0x30eb
0x3336 0x3336 0x30d8 0x30af 0x30bf 0x30fc 0x30eb 0x3336 0x30d8 0x30af 0x30bf 0x30fc 0x30eb

次は「ヴ」という一文字と,「ウ」と「 ゙ 」 からなる合字である「ヴ」についてどのように処理されるか分析します.この2つの文字は人間の目には同じに見えますが,一文字「ヴ」の文字コードは「0x30f4」で,合字「ヴ」の文字コードは「0x30a6 0x3099」であることから,内部的には同じではありません(これは検索などで期待した結果にならない可能性があることを意味しています).しかしながら, unicodedata.normalize() 関数を使うとどちらかに統一できることがわかります.具体的には,'NFC''NFKC' を使うと,つまりここの説明にある正準等価を使うと,一文字「ヴ」に正規化され,'NFD''NFKD'互換等価を使うと合字「ヴ」に正規化されます.この結果からも日本語文字列を取り扱う場合は 'NFKC' が最も適していると考えられます.

文字 処理なし 'NFC' 'NFKC' 'NFD' 'NFKD'
0x30f4 0x30f4 0x30f4 0x30a6 0x3099 0x30a6 0x3099
ヴ(合字) 0x30a6 0x3099 0x30f4 0x30f4 0x30a6 0x3099 0x30a6 0x3099

次は合字「」が unicodedata.normalize() 関数でどのように処理されるか分析します.なお Mac では option + shift + 5 で「」を入力できます.次の表から 'NFKC''NFKD' を指定すると,「f」と「i」に分割されることがわかります.なお,合字「」は英語の学術論文PDFファイルの中などでも頻繁に利用されています.見た目は美しくなるのですが,PDFファイル内を「fi」で検索しても「」がヒットしないという欠点があります.つまり,検索システムを開発するときに検索対象文字列の正規化を適切に行うと,より精度の高い検索システムができることになります.

文字(列) 処理なし 'NFC' 'NFKC' 'NFD' 'NFKD'
fi 0x66 0x69 0x66 0x69 0x66 0x69 0x66 0x69 0x66 0x69
0xfb01 0xfb01 0x66 0x69 0xfb01 0x66 0x69

さらに,ドイツ語のウムラウト「ü」,ノルウェー語などの上リング付き「å」が unicodedata.normalize() 関数でどのように処理されるか分析します.なお Mac では option + u のあとに u で「ü」を入力でき,option + a で「å」を入力できます.次の表から 互換等価である 'NFD''NFKD' を指定すると「u」や「a」とウムラウト(またはリング)に分割されることがわかります.

文字 処理なし 'NFC' 'NFKC' 'NFD' 'NFKD'
u 0x75 0x75 0x75 0x75 0x75
ü 0xfc 0xfc 0xfc 0x75 0x308 0x75 0x308
a 0x61 0x61 0x61 0x61 0x61
å 0xe5 0xe5 0xe5 0x61 0x30a 0x61 0x30a

以上,まとめると日本語の文字列を扱う場合には 'NFKC' を指定するのが最も良さそうです.欧州のウムラウトなどを扱う場合にはその目的によって 'NFKC''NFKD' を使い分ける必要がありそうです.さらに,日本,中国,台湾,韓国で使われる漢字については,同じ形や同じ意味の漢字が言語ごとにそれぞれ Unicode 表の別の場所に定義されている関係で,その取扱は難しそうです.このような問題については,京都のある大学の先生と頻繁に議論していますが,調べてみると根深い問題が潜んでいます.

目次に戻る