移除python unicode字符串中重音符号的最佳方法是什么?

What is the best way to remove accents in a Python unicode string?

我在python中有一个unicode字符串,我想删除所有重音符号(音调符号)。

我在网上找到了一个在Java中实现这一点的优雅方法:

  • 将Unicode字符串转换为其长规格化形式(字母和音调符号用单独的字符)
  • 删除Unicode类型为"音调符号"的所有字符。
  • 我需要安装一个库(如pyicu)还是只安装python标准库就可以做到这一点?那Python3呢?

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


    unidecode是正确的答案。它将任何Unicode字符串转换为最接近的ASCII文本表示形式。

    例子:

    1
    2
    3
    4
    5
    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
    2
    3
    4
    import unicodedata
    def strip_accents(s):
       return ''.join(c for c in unicodedata.normalize('NFD', s)
                      if unicodedata.category(c) != 'Mn')

    这也适用于希腊字母:

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

    字符类别"mn"代表Nonspacing_Mark,类似于unicodedata.combing在miniquark的答案中(我没有想到unicodedata.combing,但它可能是更好的解决方案,因为它更明确)。

    记住,这些操作可能会显著改变文本的含义。重音符号、斜线符号等不是"装饰"。


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

    1
    2
    3
    4
    5
    6
    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字符。

    编辑:这就是诀窍:

    1
    2
    3
    4
    5
    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.combining(c)将返回true,这主要是因为它是一个音调符号。

    编辑2:remove_accents需要一个Unicode字符串,而不是字节字符串。如果您有一个字节字符串,那么您必须将它解码成这样的Unicode字符串:

    1
    2
    3
    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)


    实际上,我在与项目兼容的python 2.6、2.7和3.4上工作,我必须从自由用户条目创建ID。

    多亏了你,我创造了一个神奇的功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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

    结果:

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


    这不仅处理重音,还处理"笔画"(如?等):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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'。这取决于你到底想做什么。我有时需要去除重音来实现字典的排序顺序。


    对于@miniquark的回答:

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

    Montréal, über, 12.89, Mère, Fran?oise, no?l, 889

    我必须加入行23,才能让它工作(我在一张python罚单中找到),并加入@jabba的评论:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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)

    结果:

    1
    2
    3
    4
    5
    6
    7
    Montreal
    uber
    12.89
    Mere
    Francoise
    noel
    889

    (注意:我使用的是Mac OS X 10.8.4和Python 2.7.3)


    gensim.utils.deaccent(文本)摘自gensim-人类主题建模:

    deaccent("?éf chomutovskych komunist? dostal po?tou bíly prá?ek")
    'Sef chomutovskych komunistu dostal postou bily prasek'

    另一个解决方案是unidecode。

    不是说建议的单码数据解决方案通常只删除某些字符中的重音(例如,它将'?'转换为'',而不是转换为'l'


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

    我认为更安全的做法是明确说明你想去掉哪些符号:

    1
    2
    3
    4
    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))