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

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

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

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

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


当前回答

这个怎么样:

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_Mark,它类似于MiniQuark回答中的unicodedata. combined(我没有想到unicodedata. combined,但它可能是更好的解决方案,因为它更显式)。

请记住,这些操作可能会极大地改变文本的含义。重音、变音等都不是“装饰”。

其他回答

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

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

Gensim .utils.deaccent(text)来自Gensim -人类主题建模:

'Sef chomutovskych komunistu dostal postou bily prasek'

另一种解决方案是unicode。

请注意,建议的unicodedata解决方案通常只删除某些字符中的重音(例如,它将“ova”变成了“”,而不是“l”)。

在我看来,所提出的解决办法不应该是公认的答案。原来的问题是要求去掉重音,所以正确的答案应该只去掉重音,而不是去掉重音加上其他未指明的变化。

简单地观察这段代码的结果,这是公认的答案。我把“Málaga”改成了“Málagueña”:

accented_string = u'Málagueña'
# accented_string is of type 'unicode'
import unidecode
unaccented_string = unidecode.unidecode(accented_string)
# unaccented_string contains 'Malaguena'and is of type 'str'

有一个额外的更改(ñ -> n),这在OQ中没有请求。

一个简单的函数,以较低的形式完成所请求的任务:

def f_remove_accents(old):
    """
    Removes common accent characters, lower form.
    Uses: regex.
    """
    new = old.lower()
    new = re.sub(r'[àáâãäå]', 'a', new)
    new = re.sub(r'[èéêë]', 'e', new)
    new = re.sub(r'[ìíîï]', 'i', new)
    new = re.sub(r'[òóôõö]', 'o', new)
    new = re.sub(r'[ùúûü]', 'u', new)
    return new

import unicodedata
from random import choice

import perfplot
import regex
import text_unidecode


def remove_accent_chars_regex(x: str):
    return regex.sub(r'\p{Mn}', '', unicodedata.normalize('NFKD', x))


def remove_accent_chars_join(x: str):
    # answer by MiniQuark
    # https://stackoverflow.com/a/517974/7966259
    return u"".join([c for c in unicodedata.normalize('NFKD', x) if not unicodedata.combining(c)])


perfplot.show(
    setup=lambda n: ''.join([choice('Málaga François Phút Hơn 中文') for i in range(n)]),
    kernels=[
        remove_accent_chars_regex,
        remove_accent_chars_join,
        text_unidecode.unidecode,
    ],
    labels=['regex', 'join', 'unidecode'],
    n_range=[2 ** k for k in range(22)],
    equality_check=None, relative_to=0, xlabel='str len'
)

unidcode是正确的答案。它将任何unicode字符串音译为最接近的ascii文本表示形式。

例子:

>>> from unidecode import unidecode
>>> unidecode('kožušček')
'kozuscek'
>>> unidecode('北亰')
'Bei Jing '
>>> unidecode('François')
'Francois'