我在Python中有一个Unicode字符串,我想删除所有的重音(变音符)。

我在网上找到了一个优雅的方法(在Java中):

将Unicode字符串转换为它的长规范化形式(使用单独的字符表示字母和变音符) 删除所有Unicode类型为“变音符”的字符。

我需要安装一个库,如pyICU或这是可能的Python标准库?那么python3呢?

重要提示:我希望避免使用从重音字符到非重音对应字符的显式映射的代码。


当前回答

有些语言将变音符组合成语言字母,并将重音变音符组合成重音。

我认为更安全的做法是明确指定你想要剥离的变量:

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))

其他回答

对于@ mini夸克的回答:

我试图读取一个csv文件,它是一半法语(包含重音),也有一些字符串,最终将成为整数和浮动。 作为测试,我创建了一个test.txt文件,如下所示:

M, Montré艾尔,关于12.89太阳è弗兰çoise,不889,ël

我必须包括第2行和第3行才能让它工作(这是我在python ticket中找到的),以及加入@Jabba的评论:

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)

这里已经有很多答案,但之前没有考虑过:使用sklearn

from sklearn.feature_extraction.text import strip_accents_ascii, strip_accents_unicode

accented_string = u'Málagueña®'

print(strip_accents_unicode(accented_string)) # output: Malaguena®
print(strip_accents_ascii(accented_string)) # output: Malaguena

如果您已经在使用sklearn处理文本,这一点特别有用。这些是由CountVectorizer等类内部调用的函数,用于规范化字符串:当使用strip_accent ='ascii'时,则调用strip_accents_ascii;当使用strip_accent ='unicode'时,则调用strip_accents_unicode。

更多的细节

最后,考虑文档字符串中的这些细节:

Signature: strip_accents_ascii(s)
Transform accentuated unicode symbols into ascii or nothing

Warning: this solution is only suited for languages that have a direct
transliteration to ASCII symbols.

and

Signature: strip_accents_unicode(s)
Transform accentuated unicode symbols into their simple counterpart

Warning: the python-level loop and join operations make this
implementation 20 times slower than the strip_accents_ascii basic
normalization.

这不仅可以处理重音,还可以处理“笔画”(如ø等):

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代码)。

如果您希望获得类似Elasticsearch的ascii折叠过滤器的功能,您可能需要考虑fold-to-ascii,这是[本身]…

Apache Lucene ASCII折叠过滤器的Python端口,它将字母、数字和符号Unicode字符转换为不属于前127个ASCII字符的字符(“基本拉丁”Unicode块),如果它们存在的话。

下面是上面提到的一个例子:

from fold_to_ascii import fold
s = u'Astroturf® paté'
fold(s)
> u'Astroturf pate'
fold(s, u'?')
> u'Astroturf? pate'

编辑:fold_to_ascii模块似乎可以很好地规范化基于拉丁的字母;然而,不可映射的字符将被删除,这意味着该模块将减少中文文本,例如,空字符串。如果您想保留中文、日语和其他Unicode字母,可以考虑使用上面@mo-han的remove_accent_chars_regex实现。

有些语言将变音符组合成语言字母,并将重音变音符组合成重音。

我认为更安全的做法是明确指定你想要剥离的变量:

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))