Каков наилучший способ удалить акценты в строке Unicode Python? - PullRequest
424 голосов
/ 06 февраля 2009

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

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

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

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

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

Ответы [ 8 ]

352 голосов
/ 14 апреля 2010

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'
250 голосов
/ 06 февраля 2009

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

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

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

129 голосов
/ 06 февраля 2009

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

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

Edit 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)
28 голосов
/ 24 июля 2015

На самом деле я работаю над проектными 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'
16 голосов
/ 21 марта 2013

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

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)

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

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

11 голосов
/ 12 июня 2013

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

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

Монреаль, Юбер, 12,89, Мере, Франсуаза, Ноэль, 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)

10 голосов
/ 30 января 2018

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

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

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

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

1 голос
/ 24 июля 2015

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

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

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