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

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

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

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

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

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

9 ответов


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'

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

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

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


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

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)

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

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

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 файл, который выглядел так:

Монреаль, ü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)


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

Для Python 3.X

print (search_string)

Для Python 2.X

print search_string

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

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

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

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


некоторые языки имеют сочетание диакритики как буквы языка и диакритики акцента, чтобы указать акцент.

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

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