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

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

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

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

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


当前回答

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

简单地观察这段代码的结果,这是公认的答案。我把“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

其他回答

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

'Sef chomutovskych komunistu dostal postou bily prasek'

另一种解决方案是unicode。

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

对于@ 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)

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

简单地观察这段代码的结果,这是公认的答案。我把“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

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

它工作得很好(例如,对于法语),但我认为第二步(删除重音)可能比删除非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)])

如果字符c可以与前面的字符组合,主要是如果它是一个变音符,则unicodedata. combined (c)将返回true。

编辑2:remove_accent需要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)

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