删除Python unicode字符串中重音的最佳方法是什么?

时间:2009-02-05 21:10:41

标签: python python-3.x unicode python-2.x diacritics

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

我在网上找到了一种在Java中实现这一目标的优雅方式:

  1. 将Unicode字符串转换为长标准化形式(字母和变音符号使用单独的字符)
  2. 删除Unicode类型为“变音符号”的所有字符。
  3. 我是否需要安装pyICU等库?或者只使用python标准库?那python 3呢?

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

10 个答案:

答案 0 :(得分:348)

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'

答案 1 :(得分:249)

这个怎么样:

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

character category“Mn”代表Nonspacing_Mark,类似于MiniQuark答案中的unicodedata.combining(我没想到unicodedata.combining,但它可能是更好的解决方案,因为它更明确了。)

请记住,这些操作可能会显着改变文本的含义。口音,变音等不是“装饰”。

答案 2 :(得分:129)

我刚在网上找到了这个答案:

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)])
如果字符unicodedata.combining(c)可以与前面的字符组合,

c将返回true,主要是因为它是变音符号。

编辑2 remove_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)

答案 3 :(得分:26)

实际上我在项目兼容的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'

答案 4 :(得分:16)

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

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(unicode(char))
    cutoff = desc.find(' WITH ')
    if cutoff != -1:
        desc = desc[:cutoff]
    return ud.lookup(desc)

这是我能想到的最优雅的方式(亚历克西斯在本页的评论中已经提到过),虽然我认为它确实不是很优雅。

由于其unicode名称不包含'WITH',因此仍然存在特殊字母,这些字母不会由此处理,例如翻页和倒置字母。这取决于你想要做什么。我有时需要强调剥离来实现字典排序。

答案 5 :(得分:11)

回应@ MiniQuark的回答:

我试图读取一个半法语(包含重音符号)的csv文件以及一些最终会变成整数和浮点数的字符串。 作为测试,我创建了一个test.txt文件,如下所示:

  

蒙特利尔,über,12.89,Mère,Françoise,noël,889

我必须包含行23以使其工作(我在python票证中找到),以及合并@ 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)

答案 6 :(得分:10)

来自gensim.utils.deaccent(text)

Gensim - topic modelling for humans

deaccent("Šéf chomutovských komunistů dostal poštou bílý prášek") 'Sef chomutovskych komunistu dostal postou bily prasek'

另一个解决方案是unidecode

不建议使用 unicodedata 的建议解决方案通常仅删除某些字符中的重音(例如,它将'ł'变为'',而不是'l')。

答案 7 :(得分:6)

perfplot

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

答案 8 :(得分:1)

有些语言将变音符号作为语言字母和重音变音符号组合以指定重音。

我认为明确指定要剥离的diactrics是更安全的:

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

答案 9 :(得分:1)

如果您希望获得类似于 Elasticsearch 的 asciifolding 过滤器的功能,您可能需要考虑 fold-to-ascii,它[本身]...

<块引用>

Apache Lucene ASCII 折叠过滤器的 Python 端口,可将不在前 127 个 ASCII 字符(“基本拉丁语”Unicode 块)中的字母、数字和符号 Unicode 字符转换为 ASCII 等效字符(如果存在)。< /p>

以下是上述页面的示例:

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

EDITfold_to_ascii 模块对于标准化拉丁字母似乎很有效;然而,不可映射的字符被删除,这意味着该模块将减少中文文本,例如,为空字符串。如果您想保留中文、日语和其他 Unicode 字母,请考虑使用上面@mo-han 的 remove_accent_chars_regex 实现。