Каков наилучший способ удаления акцентов в строке юникода Python?

У меня есть строка Unicode в Python, и я хотел бы удалить все акценты (диакритики).

Я нашел в Интернете элегантный способ сделать это в Java:

  • преобразует строку Unicode в свою длинную нормализованную форму (с отдельным символом для букв и диакритики)
  • удалить все символы, тип Unicode которых "диакритический".

Нужно ли устанавливать библиотеку, такую ​​как pyICU, или это возможно только с стандартной библиотекой python? А как насчет python 3?

Важное примечание. Я хотел бы избежать кода с явным сопоставлением от акцентированных символов к их неактивному аналогу.

Ответ 1

Unidecode - правильный ответ для этого. Он транслитерирует любую строку юникода в максимально возможное представление в тексте 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'

Ответ 2

Как насчет этого:

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, что похоже на unicodedata.combining в ответе MiniQuark (я не думал unicodedata.combining, но это, вероятно, лучшее решение, потому что оно более явное).

И имейте в виду, что эти манипуляции могут значительно изменить смысл текста. Акценты, Umlauts и т.д. Не являются "украшением".

Ответ 3

Я нашел этот ответ в Интернете:

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) вернет true, если символ c можно комбинировать с предыдущим символом, то есть в основном, если он диакритический.

Изменить 2: remove_accents ожидает строку 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)

Ответ 4

На самом деле я работаю над совместимыми с проектом версиями python 2.6, 2.7 и 3.4, и мне приходится создавать идентификаторы из бесплатных пользовательских записей.

Благодаря вам я создал эту функцию, которая творит чудеса.

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'

Ответ 5

Я удивлен, что никто не предложил простой:

from unidecode import unidecode

s="Montréal, über, 12.89, Mère, Françoise, noël, 889"
#s.encode("ascii")  #doesn't work - traceback
t=unidecode(s)
t.encode("ascii")  #works fine, because all non-ASCII from s are replaced with their equivalents
print(t)  #gives: 'Montreal, uber, 12.89, Mere, Francoise, noel, 889'

Вы можете скачать unidecode lib здесь.

Ответ 6

Это обрабатывает не только акценты, но и "штрихи" (как в ø и т.д.):

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)

Это самый элегантный способ, о котором я могу думать (и он был упомянут alexis в комментарии на этой странице), хотя я не думаю, что это действительно очень элегантно.

Есть еще специальные буквы, которые не обрабатываются этим, такие как повернутые и перевернутые буквы, поскольку их имя юникода не содержит "WITH". Это зависит от того, что вы хотите сделать в любом случае. Я иногда нуждался в снятии акцентов для достижения порядка сортировки словаря.

Ответ 7

В ответ на ответ @MiniQuark:

Я пытался прочитать в файле csv, который был наполовину французским (содержащий акценты), а также некоторые строки, которые в конечном итоге станут целыми числами и плавают. В качестве теста я создал файл test.txt, который выглядел следующим образом:

Монреаль, über, 12.89, Mère, Françoise, noël, 889

Мне пришлось включать строки 2 и 3, чтобы заставить его работать (что я нашел в билете на 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)

Ответ 8

import unicodedata
s = 'Émission'
search_string = ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))


print (search_string)

Ответ 9

gensim.utils.deaccent (текст) от Gensim - тема для людей:

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

Другим решением является unidecode.

Не то, чтобы предлагаемое решение с unicodedata обычно удаляет акценты только в некотором символе (например, оно превращает 'ł' в '', а не в 'l').

Ответ 10

Некоторые языки объединяют диакритические знаки как языковые буквы и диакритические знаки акцента для указания акцента.

Я думаю, что более безопасно явно указывать, какие диалекты вы хотите разделить:

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