Pythonユニコード文字列のアクセントを削除する最良の方法は何ですか?


507

PythonにUnicode文字列があり、すべてのアクセント(発音区別符号)を削除したいと思います。

私はJavaでこれを行うエレガントな方法をWebで見つけました:

  1. Unicode文字列をその長い正規化された形式に変換します(文字と発音区別符号に別の文字を使用)
  2. Unicodeタイプが「発音区別符号」であるすべての文字を削除します。

pyICUなどのライブラリをインストールする必要がありますか、それともpython標準ライブラリだけで可能ですか?そして、python 3はどうですか?

重要な注意:アクセント記号付き文字からアクセント記号なしの対応する文字への明示的なマッピングを含むコードは避けたいです。

回答:


448

Unidecodeはこれに対する正しい答えです。これは、任意のUnicode文字列をASCIIテキストで最も近い可能な表現に音訳します。

例:

accented_string = u'Málaga'
# accented_string is of type 'unicode'
import unidecode
unaccented_string = unidecode.unidecode(accented_string)
# unaccented_string contains 'Malaga'and is of type 'str'

67
中国語でうまく機能するようですが、フランス語の名前「François」を変換すると、残念ながら「FranASSois」が得られます。これは、より自然な「Francois」と比較するとあまり良くありません。
Eric O Lebigot、2011

10
あなたが達成しようとしていることに依存します。たとえば、現在検索を行っており、ギリシャ語/ロシア語/中国語を文字変換したくありません。「ą/ę/ś/ć」を「a / e / s / c」に置き換えたいだけです
kolinko

58
@EOL unidecodeは、 "François"のような文字列にユニコードオブジェクトを渡す場合に最適です。プレーンなバイト文字列で試したようです。
Karl Bartel 2012

26
unidecode> = 0.04.10(Dec 2012)はGPLであることに注意してください。以前のバージョンを使用するか、より寛容なライセンスが必要で、やや悪い実装に耐えられる場合は、github.com / kmike / text-unidecodeを確認してください。
ミハイルコロボフ2014

10
unidecode置き換え°deg。アクセントを取り除くだけではありません。
Eric Duminil 2017

274

これはどう:

import unicodedata
def strip_accents(s):
   return ''.join(c for c in unicodedata.normalize('NFD', s)
                  if unicodedata.category(c) != 'Mn')

これはギリシャ文字でも機能します。

>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>> 

文字カテゴリ「Mnは」の略Nonspacing_MarkMiniQuarkの答えにunicodedata.combiningに似ているが、(私はunicodedata.combining考えていなかったが、それはより明確だから、それは、おそらくよりよい解決策です)。

また、これらの操作により、テキストの意味が大幅に変わる可能性があることに注意してください。アクセント、ウムラウトなどは「装飾」ではありません。


6
残念ながら、「ł」は「ラテン小文字Lストロークあり」という名前ですが、これらは合成文字ではありません。構文解析unicodedata.nameを使用してゲームをプレイするか、分解して、似たようなテーブルを使用する必要があります-とにかくギリシャ語の文字に必要です(Αは単に「GREEK CAPITAL LETTER ALPHA」です)。
アレクシス

2
@andi、私はあなたが何を言いたいのかわからないです。電子メールのやり取りは、私が上で書いたものを反映しています。文字「ł」はアクセント付きの文字ではない(そしてUnicode標準では1つとして扱われない)ため、分解されません。
アレクシス2014年

2
@alexis(後のフォローアップ):これはギリシャ語でも完全に機能します。「ギリシャの大文字のアルファとDASIAおよびVARIA」は、期待どおりに「ギリシャの大文字のアルファ」に正規化されます。あなたが参照している場合を除き音訳 ...「削除アクセント」と同じではありません(。例えば、「α」→「」)、
レンツ

@lenz、私はギリシャ語からアクセントを取り除くことについて話しているのではなく、エルの「ストローク」について話していました。これは発音区別符号ではないので、単純なエルに変更することはギリシャ語のアルファをに変更することと同じAです。したくない場合は、それを行わないでください。ただし、どちらの場合も、ラテン語(ほぼ)に似たものに置き換えます。
アレクシス

ほとんどうまく動作します:)しかし、例ではßASCII ssに変換されません。unidecode事故を避けるために私はまだ使います。
アート

146

私はこの答えをウェブで見つけました:

import unicodedata

def remove_accents(input_str):
    nfkd_form = unicodedata.normalize('NFKD', input_str)
    only_ascii = nfkd_form.encode('ASCII', 'ignore')
    return only_ascii

これは問題なく動作しますが(フランス語など)、2番目のステップ(アクセントの削除)は、一部の言語(ギリシャ語など)で失敗するため、非ASCII文字を削除するよりもうまく処理できると思います。最良の解決策は、おそらく発音区別符号としてタグ付けされたUnicode文字を明示的に削除することです。

編集:これはトリックを行います:

import unicodedata

def remove_accents(input_str):
    nfkd_form = unicodedata.normalize('NFKD', input_str)
    return u"".join([c for c in nfkd_form if not unicodedata.combining(c)])

unicodedata.combining(c)文字cを前の文字と組み合わせることができる場合、つまり、主に発音区別符号の場合、trueを返します。

編集2remove_accents期待したUnicode文字列ではなく、バイト文字列を。バイト文字列がある場合は、次のようなUnicode文字列にデコードする必要があります。

encoding = "utf-8" # or iso-8859-15, or cp1252, or whatever encoding you use
byte_string = b"café"  # or simply "café" before python 3.
unicode_string = byte_string.decode(encoding)

5
'utf8'をユニコードに追加する必要がありました:nkfd_form = unicodedata.normalize('NFKD', unicode(input_str, 'utf8'))
Jabba

@Jabba:, 'utf8'端末の入力をテストする場合に必要な「セーフティネット」です(デフォルトではUnicodeを使用しません)。しかし、通常、あなたはしていない持っているあなたはアクセントを削除している場合は、それ以来、それを追加するinput_str可能性が高いすでにUTF8になることです。しかし、安全であることは害にはなりません。
MestreLion 2012

1
@rbp:remove_accents通常の文字列の代わりにUnicode文字列を渡す必要があります( "é"の代わりにu "é")。通常の文字列をに渡したremove_accentsため、文字列をUnicode文字列に変換しようとすると、デフォルトのasciiエンコーディングが使用されました。このエンコーディングは、値が> 127のバイトをサポートしていません。シェルで「é」と入力すると、OSはそれをおそらくUTF-8またはWindowsコードページエンコーディングでエンコードし、バイト数が127を超えていました。ユニコードへの変換を削除するために、関数を変更します。非ユニコード文字列が渡された場合、より明確に爆撃されます。
ミニクォーク

1
完全に機能した@MiniQuark >>> remove_accents(unicode( 'é'))
rbp

1
この答えは、大規模なデータセットで最高の結果をもたらしました。唯一の例外は「-」です-unicodedataはそれに触れません!
s29

43

実際、私はプロジェクト互換のpython 2.6、2.7、3.4で作業しており、無料のユーザーエントリからIDを作成する必要があります。

おかげで、私は不思議に機能するこの関数を作成しました。

import re
import unicodedata

def strip_accents(text):
    """
    Strip accents from input String.

    :param text: The input string.
    :type text: String.

    :returns: The processed String.
    :rtype: String.
    """
    try:
        text = unicode(text, 'utf-8')
    except (TypeError, NameError): # unicode is a default on python 3 
        pass
    text = unicodedata.normalize('NFD', text)
    text = text.encode('ascii', 'ignore')
    text = text.decode("utf-8")
    return str(text)

def text_to_id(text):
    """
    Convert input text to id.

    :param text: The input string.
    :type text: String.

    :returns: The processed String.
    :rtype: String.
    """
    text = strip_accents(text.lower())
    text = re.sub('[ ]+', '_', text)
    text = re.sub('[^0-9a-zA-Z_-]', '', text)
    return text

結果:

text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'

2
Py2.7では、Unicode文字列エラーをで渡しtext = unicode(text, 'utf-8')ます。そのための回避策は追加することでしたexcept TypeError: pass
Daniel Reis

とてもうるさい!私の場合は働いた。Umaseleçãode poesia brasileira para desenvolver a capacidade de escuta dos alunos idiomaPortuguês。
アーロン

23

これはアクセントだけでなく、「ストローク」(øなど)も処理します。

import unicodedata as ud

def rmdiacritics(char):
    '''
    Return the base character of char, by "removing" any
    diacritics like accents or curls and strokes and the like.
    '''
    desc = ud.name(char)
    cutoff = desc.find(' WITH ')
    if cutoff != -1:
        desc = desc[:cutoff]
        try:
            char = ud.lookup(desc)
        except KeyError:
            pass  # removing "WITH ..." produced an invalid name
    return char

これは私が考えることができる最もエレガントな方法です(そしてそれはこのページのコメントでアレクシスによって言及されました)確かにそれは非常にエレガントではないと思います。実際、Unicodeの名前は実際には単なる名前であり、一貫性などを保証するものではないため、コメントで指摘されているように、これはハックのようなものです。

unicode名に 'WITH'が含まれていないため、回転文字や反転文字など、これによって処理されない特殊文字がまだあります。それはとにかく何をしたいかに依存します。辞書の並べ替え順序を実現するために時々アクセントの除去が必要でした。

ノートの編集:

コメントから組み込まれた提案(ルックアップエラーの処理、Python-3コード)。


8
新しいシンボルが存在しない場合は、例外をキャッチする必要があります。たとえば、垂直方向の塗りつぶしのある四角形がありますが、四角形はありません。(このコードはUMBRELLA WITH RAIN DROPS☔をUMBRELLA☂に変換することは言うまでもありません)。
janek37 2015

これは、利用可能な文字のセマンティックな説明を利用する上でエレガントに見えます。unicodePython 3で関数を呼び出す必要があるのでしょうか?の代わりに正規表現を厳しくfindすることで、上記のコメントで言及されているすべての問題を回避できると思います。また、メモ化は、それが重要なコードパスである場合のパフォーマンスに役立ちます。
matanster

1
@matansterいいえ、これはPython-2時代の古い答えです。unicode型キャストは、もはやこの問題に対する普遍的な、エレガントな解決策はありません私の経験では、いずれの場合ではPython 3に充当されていません。アプリケーションによっては、どのアプローチにも長所と短所があります。のような品質の高いツールunidecodeは、手作りのテーブルに基づいています。一部のリソース(テーブル、アルゴリズム)は、Unicodeによって提供されます。照合用。
レンツ

1
繰り返しますが、上記(py3)の内容は次のとおりです。1)unicode(char)-> char 2)試行:return ud.lookup(desc)を除き、KeyError:return char
mirek

@mirek正解です。このスレッドは非常に人気があるため、この回答は更新/改善に値します。編集しました。
Lenz

15

@MiniQuarkの答えに応じて:

私は半分フランス語(アクセントを含む)のcsvファイルと、最終的に整数と浮動小数点になる文字列を読み込もうとしていました。テストとして、次のtest.txtようなファイルを作成しました。

モントリオール、über、12.89、Mère、Françoise、noël、889

@Jabbaのコメントを組み込むだけでなく、(Pythonチケットで見つけた)行を含め2て機能3させる必要がありました。

import sys 
reload(sys) 
sys.setdefaultencoding("utf-8")
import csv
import unicodedata

def remove_accents(input_str):
    nkfd_form = unicodedata.normalize('NFKD', unicode(input_str))
    return u"".join([c for c in nkfd_form if not unicodedata.combining(c)])

with open('test.txt') as f:
    read = csv.reader(f)
    for row in read:
        for element in row:
            print remove_accents(element)

結果:

Montreal
uber
12.89
Mere
Francoise
noel
889

(注:私はMac OS X 10.8.4を使用していて、Python 2.7.3を使用しています)


1
remove_accentsUnicode文字列からアクセントを削除するためのものでした。バイト文字列が渡された場合は、でUnicode文字列に変換しようとしunicode(input_str)ます。これは、「ascii」であるpythonのデフォルトのエンコーディングを使用します。ファイルはUTF-8でエンコードされているため、これは失敗します。2行目と3行目では、PythonのデフォルトのエンコーディングをUTF-8に変更しているため、ご存じのように機能します。別のオプションは、通過させることであるremove_accentsUnicode文字列:削除線2と3を、そして最後の行に置き換えるelementことでelement.decode("utf-8")。私はテストしました:それは動作します。これをより明確にするために、回答を更新します。
MiniQuark 2013年

素敵な編集、良い点。(別のメモ:私が気付いた本当の問題は、私のデータファイルが明らかにでエンコードされているiso-8859-1ことです。残念ながら、この関数で作業することはできません!)
aseagram

aseagram:「utf-8」を「iso-8859-1」に置き換えるだけで動作します。Windowsを使用している場合は、代わりに「cp1252」を使用する必要があります。
MiniQuark 2013年

ところで、reload(sys); sys.setdefaultencoding("utf-8")Windowsシステムには時々お勧めの怪しいハックです。詳細については、stackoverflow.com / questions / 28657010 /…を参照してください。
PM 2Ring

14

gensim.utils.deaccent(テキスト)からGensim -人間のためのトピックモデリング

'Sef chomutovskych komunistu dostal postou bily prasek'

別の解決策はユニコードですです。

提案された解決策という注意のUnicodeDataは、一般的に(例えば、それが変わるだけで、いくつかの文字にアクセントを削除'ł'''というに比べ、'l')。


1
deaccentł代わりにまだ与えますl
lcieslak

インストールNumPySciPyてアクセントを削除する必要はありません。
ヌーノアンドレ

gensimリファレンスをありがとう!unidecodeと比較すると(速度または精度の点で)どうですか?
エティエンヌキンツラー

3

一部の言語では、発音区別符号を言語文字として組み合わせ、アクセント発音区別符号を使用してアクセントを指定しています。

ストリップしたいダイアトリクスを明示的に指定する方が安全だと思います。

def strip_accents(string, accents=('COMBINING ACUTE ACCENT', 'COMBINING GRAVE ACCENT', 'COMBINING TILDE')):
    accents = set(map(unicodedata.lookup, accents))
    chars = [c for c in unicodedata.normalize('NFD', string) if c not in accents]
    return unicodedata.normalize('NFC', ''.join(chars))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.