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

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

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

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

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

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

5.02.2009 21:10:40
8 ОТВЕТОВ
РЕШЕНИЕ

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'
437
21.07.2017 17:23:40
Кажется, хорошо работает с китайцами, но преобразование французского имени «Франсуа», к сожалению, дает «ФранАссуа», что не очень хорошо, по сравнению с более естественным «Франсуа».
Eric O Lebigot 17.09.2011 14:56:34
зависит от того, чего ты пытаешься достичь. например, я сейчас делаю поиск и не хочу транслитерировать греческий / русский / китайский, я просто хочу заменить "ą / ę / ś / ć" на "a / e / s / c"
kolinko 31.03.2012 18:15:10
@EOL unidecode отлично подходит для таких строк, как "François", если вы передаете ему юникод-объекты. Похоже, вы пробовали с простой строкой байтов.
Karl Bartel 30.04.2012 09:38:42
Обратите внимание, что unidecode> = 0.04.10 (декабрь 2012) - это GPL. Используйте более ранние версии или проверьте github.com/kmike/text-unidecode, если вам нужна более разрешительная лицензия, и ее реализация может быть несколько хуже.
Mikhail Korobov 23.02.2014 22:27:52
unidecodeзаменяется °на deg. Это больше, чем просто удаление акцентов.
Eric Duminil 28.04.2017 12:02:31

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

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, потому что это не получится для некоторых языков (например, греческого). Лучшим решением, вероятно, будет явное удаление символов Юникода, помеченных как диакритические знаки.

Изменить : это делает трюк:

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)
142
14.09.2015 17:01:00
Я должен был добавить 'utf8' в Unicode:nkfd_form = unicodedata.normalize('NFKD', unicode(input_str, 'utf8'))
Jabba 8.01.2012 23:27:38
@Jabba: , 'utf8'это «сеть безопасности», необходимая, если вы тестируете ввод в терминале (который по умолчанию не использует юникод). Но обычно вам не нужно добавлять его, так как если вы удаляете акценты, то, input_strскорее всего, уже будет utf8. Впрочем, быть в безопасности не вредно.
MestreLion 17.04.2012 23:15:35
@rbp: вы должны передать строку в юникоде remove_accentsвместо обычной строки (u «é» вместо «é»). Вы передали обычную строку remove_accents, поэтому при попытке преобразовать вашу строку в строку Unicode asciiбыла использована кодировка по умолчанию . Эта кодировка не поддерживает ни одного байта, значение которого> 127. Когда вы ввели «é» в своей оболочке, ваша ОС закодировала это, вероятно, с помощью UTF-8 или некоторой кодировки Windows Code Page и включило байты> 127. Я изменю свою функцию, чтобы убрать преобразование в юникод: оно будет бомбить более четко, если пропущена не-юникодная строка.
MiniQuark 11.06.2013 10:11:42
@MiniQuark, который работал отлично >>> remove_accents (unicode ('é'))
rbp 12.06.2013 20:59:05
Этот ответ дал мне лучший результат для большого набора данных, единственное исключение - «ð» - unicodedata его не трогал!
s29 8.06.2018 02:38:41

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

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, но это, вероятно, лучшее решение, потому что это более явным).

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

270
28.07.2016 13:39:51
К сожалению, это не составные символы - хотя "ł" называется "LATIN SMALL LETTER L WITH STROKE"! Вам нужно будет либо сыграть в игры с разбором unicodedata.name, либо сломать и использовать похожий стол - который вам в любом случае понадобится для греческих букв (Α это просто «ГРЕЧЕСКАЯ КАПИТАЛЬНАЯ ПИСЬМА АЛЬФА»).
alexis 7.04.2012 11:25:32
@andi, боюсь, я не могу догадаться, что ты хочешь сказать. Обмен электронной почтой отражает то, что я написал выше: поскольку буква «ł» не является буквой с акцентом (и не рассматривается как буква в стандарте Unicode), она не имеет декомпозиции.
alexis 23.11.2014 00:12:18
@alexis (позднее продолжение): Это прекрасно работает и для греческого - например. «ГРЕЧЕСКОЕ КАПИТАЛЬНОЕ ПИСЬМО АЛЬФА С ДАЗИЕЙ И ВАРИЕЙ» нормализовано в «ГРЕЧЕСКОЕ КАПИТАЛЬНОЕ ПИСЬМО АЛЬФА», как и ожидалось. Если вы не имеете в виду транслитерацию (например, «α» → «a»), которая отличается от «удаления акцентов» ...
lenz 16.05.2016 07:41:48
@lenz, я говорил не об удалении акцентов с греческого, а о «инсульте» в аду. Поскольку это не диакритический знак, изменение его на обычный ell равносильно изменению греческой альфы на A. Если не хотите, не делайте этого, но в обоих случаях вы заменяете латиницу (почти) аналогично.
alexis 16.05.2016 17:01:56
В основном работает хорошо :) Но это не превращается ßв ascii ssв примере. Я бы все еще использовал, unidecodeчтобы избежать несчастных случаев.
Art 1.03.2017 06:53:40

Это обрабатывает не только акценты, но и «удары» (как в ø и т. Д.):

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

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

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

РЕДАКТИРОВАТЬ ПРИМЕЧАНИЕ:

Включенные предложения из комментариев (обработка ошибок поиска, код Python-3).

22
8.11.2019 18:19:51
Вы должны поймать исключение, если новый символ не существует. Например, есть квадрат с вертикальным заполнением, но нет квадратного. (не говоря уже о том, что этот код преобразует ЗОНТИК С Дождевыми каплями ☔ в ЗОНТИК ☂).
janek37 9.07.2015 09:45:41
Это выглядит элегантно при использовании семантических описаний доступных персонажей. Нам действительно нужен unicodeвызов функции с Python 3? Я думаю, что более жесткое регулярное выражение вместо этого findпозволит избежать всех проблем, упомянутых в комментарии выше, а также, памятование поможет производительности, когда это критический путь кода.
matanster 29.12.2018 14:30:01
@matanster нет, это старый ответ эпохи Python-2; unicodeнапечатанная больше не ассигновать в Python 3. В любом случае, в моем опыте нет универсального, элегантного решения этой проблемы. В зависимости от приложения любой подход имеет свои плюсы и минусы. Качественные инструменты unidecode, основанные на ручных таблицах. Некоторые ресурсы (таблицы, алгоритмы) предоставляются Unicode, например. для сопоставления.
lenz 29.12.2018 14:45:19
Я просто повторяю, что выше (py3): 1) unicode (char) -> char 2) try: return ud.lookup (desc) за исключением KeyError: return char
mirek 8.11.2019 12:50:40
@mirek вы правы: так как эта тема очень популярна, этот ответ заслуживает некоторого обновления / улучшения. Я редактировал это.
lenz 8.11.2019 18:22:30

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

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

Монреаль, über, 12.89, Mère, Франсуаза, 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)

15
12.06.2013 18:10:05
remove_accentsбыл предназначен для удаления акцентов из строки Unicode. В случае, если ему передана строка байтов, она пытается преобразовать ее в строку Unicode с помощью unicode(input_str). Это использует кодировку по умолчанию Python, которая "ascii". Так как ваш файл закодирован в UTF-8, это не получится. Строки 2 и 3 изменяют кодировку Python по умолчанию на UTF-8, так что тогда это работает, как вы узнали. Другой вариант - передать remove_accentsстроку в юникоде: удалить строки 2 и 3, а в последней строке заменить elementна element.decode("utf-8"). Я проверял: это работает. Я уточню свой ответ, чтобы прояснить ситуацию.
MiniQuark 12.06.2013 19:52:11
Хорошее редактирование, хорошая мысль. (Еще одно замечание: реальная проблема, которую я осознал, заключается в том, что мой файл данных, по-видимому, закодирован iso-8859-1, что, к сожалению, я не могу заставить работать с этой функцией!)
aseagram 12.06.2013 20:11:04
aseagram: просто замените «utf-8» на «iso-8859-1», и оно должно работать. Если вы работаете в Windows, вам, вероятно, следует использовать вместо этого «cp1252».
MiniQuark 13.06.2013 07:43:28
Кстати, reload(sys); sys.setdefaultencoding("utf-8")сомнительный хак, иногда рекомендуемый для систем Windows; см. stackoverflow.com/questions/28657010/… для получения подробной информации.
PM 2Ring 16.05.2018 13:13:19

На самом деле я работаю над проектами, совместимыми с 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'
40
23.11.2017 22:12:14
С Py2.7, передавая уже ошибки строки Юникода в text = unicode(text, 'utf-8'). Обходной путь для этого должен был добавитьexcept TypeError: pass
Daniel Reis 18.03.2016 15:56:04
Очень шумно! Работал в моем случае. Вы можете выбрать наиболее подходящую для этого концептуальную базу данных.
Aaron 10.12.2019 13:15:29

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

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

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))
3
24.07.2015 11:34:02

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

'Sef chomutovskych komunistu dostal postou bily prasek'

Другое решение - unidecode .

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

13
16.09.2019 04:23:00
deaccentвсе равно дает łвместо l.
lcieslak 10.06.2019 08:13:39
Вам не нужно устанавливать NumPyи SciPyудалять акценты.
Nuno André 13.09.2019 18:46:42
спасибо за ссылку gensim! как это сравнить с unidecode (с точки зрения скорости или точности)?
Etienne Kintzler 20.12.2019 11:38:13