類似文字問題
ハイフンとダッシュのように、別の文字ではあるが、見た目が全く同じ文字というものが存在しています。以下がその例です。
# ハイフンに見える文字と、その文字コード - 0x2d ‑ 0x2011 ‒ 0x2012 – 0x2013 - 0xff0d ─ 0x2500 ー 0x30fc # 波線に見える文字と、その文字コード 〜 0x301c ~ 0xff5e # 中点に見える文字と、その文字コード · 0xb7 ⋅ 0x22c5
見た目では区別が付きませんね。
これは結構困ります。キーボードで全角マイナスを打つときに、WindowsとMacでは別の文字が入力されるとさえ言われています。 https://www.ilovex.co.jp/Division/ITD/archives/2008/02/windowsmac.html
このような「見た目が同じでも実は別の文字」は、自然言語処理のあらゆる場面でも問題となってきます。たとえば、正規表現で電話番号をマッチさせたい時、上述の全てのハイフンを考慮しなくてはなりません。 これは大変面倒なので、事前に正規化することで特定のハイフンだけを考慮すれば良いというようにしましょう。
Pythonで正規化するコードを書いてみました。 なお、似た文字のリストはこちらを参考にさせていただきました。
Unicodeの似た文字を整理してみた - y-kawazの日記
def normalize_wavedash(text, show_conversion_rule=False): convert_from = [ u'\u007e', u'\u223c', u'\u223e', u'\u301c', u'\u3030', u'\uff5e' ] convert_to = u'\u301c' for char in convert_from: text = text.replace(char, convert_to) if not show_conversion_rule: return text print('rule of wavedash normalization') for char in convert_from: print(char, hex(ord(char)), '->', convert_to, hex(ord(convert_to))) return text def normalize_hyphen(text, show_conversion_rule=False): convert_from = [ u'\u002d', u'\u00ad', u'\u2010', u'\u2011', u'\u2012', u'\u2013', u'\u2043', u'\ufe63', u'\u2212', u'\u207b', u'\u208b', u'\uff0d', u'\u2500', u'\u2501', u'\u30fc' ] convert_to = u'\u30fc' for char in convert_from: text = text.replace(char, convert_to) if not show_conversion_rule: return text print('rule of hyphen normalization') for char in convert_from: print(char, hex(ord(char)), '->', convert_to, hex(ord(convert_to))) return text def normalize_3dots(text, show_conversion_rule=False): convert_from = [ u'\u2026', u'\u22ef' ] convert_to = u'\u2026' for char in convert_from: text = text.replace(char, convert_to) if not show_conversion_rule: return text print('rule of 3 dots normalization') for char in convert_from: print(char, hex(ord(char)), '->', convert_to, hex(ord(convert_to))) return text def normalize_dot(text, show_conversion_rule=False): convert_from = [ u'\u00b7', u'\u2022', u'\u2219', u'\u22c5', u'\u30fb', u'\uff65' ] convert_to = u'\u30fb' for char in convert_from: text = text.replace(char, convert_to) if not show_conversion_rule: return text print('rule of dot normalization') for char in convert_from: print(char, hex(ord(char)), '->', convert_to, hex(ord(convert_to))) return text if __name__ == '__main__': print(normalize_wavedash('', show_conversion_rule=True)) print(normalize_hyphen('', show_conversion_rule=True)) print(normalize_3dots('', show_conversion_rule=True)) print(normalize_dot('', show_conversion_rule=True)) text = 'やっほ〰 4月1日~4月2日 15:00∼16:00開催' print('変換前:', text) print('変換後:', normalize_wavedash(text)) print() text = 'やっほ‒ 4月1日-4月2日 15:00─16:00開催' print('変換前:', text) print('変換後:', normalize_hyphen(text)) print() text = '日時…4月1日-4月2日 時間⋯15:00─16:00' print('変換前:', text) print('変換後:', normalize_3dots(text)) print() text = '日時・4月1日-4月2日 時間・15:00─16:00' print('変換前:', text) print('変換後:', normalize_dot(text)) print()
上のコードを実行してみた結果。
rule of wavedash normalization ~ 0x7e -> 〜 0x301c ∼ 0x223c -> 〜 0x301c ∾ 0x223e -> 〜 0x301c 〜 0x301c -> 〜 0x301c 〰 0x3030 -> 〜 0x301c ~ 0xff5e -> 〜 0x301c rule of hyphen normalization - 0x2d -> ー 0x30fc 0xad -> ー 0x30fc ‐ 0x2010 -> ー 0x30fc ‑ 0x2011 -> ー 0x30fc ‒ 0x2012 -> ー 0x30fc – 0x2013 -> ー 0x30fc ⁃ 0x2043 -> ー 0x30fc ﹣ 0xfe63 -> ー 0x30fc − 0x2212 -> ー 0x30fc ⁻ 0x207b -> ー 0x30fc ₋ 0x208b -> ー 0x30fc - 0xff0d -> ー 0x30fc ─ 0x2500 -> ー 0x30fc ━ 0x2501 -> ー 0x30fc ー 0x30fc -> ー 0x30fc rule of 3 dots normalization … 0x2026 -> … 0x2026 ⋯ 0x22ef -> … 0x2026 rule of dot normalization · 0xb7 -> ・ 0x30fb • 0x2022 -> ・ 0x30fb ∙ 0x2219 -> ・ 0x30fb ⋅ 0x22c5 -> ・ 0x30fb ・ 0x30fb -> ・ 0x30fb ・ 0xff65 -> ・ 0x30fb 変換前: やっほ〰 4月1日~4月2日 15:00∼16:00開催 変換後: やっほ〜 4月1日〜4月2日 15:00〜16:00開催 変換前: やっほ‒ 4月1日-4月2日 15:00─16:00開催 変換後: やっほー 4月1日ー4月2日 15:00ー16:00開催 変換前: 日時…4月1日-4月2日 時間⋯15:00─16:00 変換後: 日時…4月1日-4月2日 時間…15:00─16:00 変換前: 日時・4月1日-4月2日 時間・15:00─16:00 変換後: 日時・4月1日-4月2日 時間・15:00─16:00
以上、今回はPythonでハイフンやマイナスなど類似文字を正規化してみました。 良い記事だと思っていただいた方は、SNSでのシェア、ブログからのリンク、「読者になる」ボタンのクリック、「★」ボタンのクリック、よろしくお願いします!