Jaki jest najlepszy sposób na usunięcie akcentów w łańcuchu Unicode Pythona?

Mam ciąg Unicode w Pythonie i chciałbym usunąć wszystkie akcenty (znaki diakrytyczne).

Znalazłem w sieci elegancki sposób na to w Javie:

  1. konwersja ciągu znaków Unicode do jego długiej znormalizowanej postaci (z oddzielnym znakiem dla liter i znaków diakrytycznych)
  2. usuń wszystkie znaki, których typem Unicode jest "diacritic".

Czy muszę instalować bibliotekę taką jak pyICU, czy jest to możliwe tylko za pomocą standardowej biblioteki Pythona? I co o Pythonie 3?

Ważna uwaga: chciałbym uniknąć kodu z wyraźnym mapowaniem ze znaków akcentowanych do ich nieakcentowanego odpowiednika.

Author: Mark Amery, 2009-02-06

9 answers

Unidecode jest poprawną odpowiedzią na to pytanie. Transliteracja dowolnego ciągu unicode na najbliższą możliwą reprezentację w tekście ascii.

Przykład:

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'
 284
Author: Christian Oudard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-21 17:23:40

A może tak:

import unicodedata
def strip_accents(s):
   return ''.join(c for c in unicodedata.normalize('NFD', s)
                  if unicodedata.category(c) != 'Mn')

To działa również na litery greckie:

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

Kategoria znaków "Mn" oznacza Nonspacing_Mark, która jest podobna do unicodedata.łączenie w odpowiedzi Miniquarka (nie myślałem o unicodedata.łączenie, ale to chyba lepsze rozwiązanie, bo jest bardziej wyraziste).

I należy pamiętać, że te manipulacje mogą znacząco zmienić znaczenie tekstu. Akcenty, Umlauty itp. nie są "dekoracją".

 230
Author: oefe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-28 13:39:51

Właśnie znalazłem taką odpowiedź w sieci:

import unicodedata

def remove_accents(input_str):
    nfkd_form = unicodedata.normalize('NFKD', input_str)
    only_ascii = nfkd_form.encode('ASCII', 'ignore')
    return only_ascii

To działa dobrze (dla francuskiego, na przykład), ale myślę, że drugi krok (usunięcie akcentów) może być obsługiwany lepiej niż opuszczenie znaków nie-ASCII, ponieważ to nie będzie w niektórych językach(Grecki, na przykład). Najlepszym rozwiązaniem byłoby prawdopodobnie jawne usunięcie znaków unicode, które są oznaczone jako diakrytyczne.

Edit : this does the trick:

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) zwróci true jeśli znak c może być łączony z poprzedzającym znakiem, to jest głównie, jeśli jest to znak diakrytyczny.

Edytuj 2: remove_accents oczekuje ciągu unicode, a nie ciągu bajtów. Jeśli masz łańcuch bajtowy, musisz go zdekodować do ciągu unicode, takiego jak:

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)
 120
Author: MiniQuark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-14 17:01:00

Właściwie pracuję na Pythonie 2.6, 2.7 i 3.4 zgodnym z projektem i muszę tworzyć ID z wolnych wpisów użytkowników.

Dzięki tobie stworzyłem tę funkcję, która czyni cuda.
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

Wynik:

text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889")
>>> 'montreal_uber_1289_mere_francoise_noel_889'
 21
Author: Jer42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-23 22:12:14

To obsługuje nie tylko akcenty, ale także "pociągnięcia" (jak w ø itp.):

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)

To najbardziej elegancki sposób, jaki mogę wymyślić (i został wspomniany przez alexis w komentarzu na tej stronie), chociaż nie sądzę, że jest bardzo elegancki.

Nadal istnieją specjalne litery, które nie są obsługiwane przez to, takie jak odwrócone i odwrócone litery, ponieważ ich nazwa unicode nie zawiera 'WITH'. To zależy od tego, co chcesz robić. Czasami potrzebowałem rozbierania akcentu dla osiągnięcie kolejności sortowania słownika.

 12
Author: lenz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-21 12:39:18

W odpowiedzi na odpowiedź @ MiniQuark:

Próbowałem przeczytać w pliku csv, który był półfrancuski (zawierający akcenty), a także kilka ciągów, które ostatecznie staną się liczbami całkowitymi i pływakami. Jako test stworzyłem plik test.txt, który wyglądał tak:

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

Musiałem dodać linie 2 i 3, Aby to zadziałało( co znalazłem w bilecie Pythona), a także włączyć @ Jabba ' s komentarz:

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)

Wynik:

Montreal
uber
12.89
Mere
Francoise
noel
889

(Uwaga: jestem na Mac OS X 10.8.4 i używam Pythona 2.7.3)

 11
Author: aseagram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-12 18:10:05
import unicodedata
s = 'Émission'
search_string = ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))

Dla Pythona 3.X

print (search_string)

Dla Pythona 2.X

print search_string
 8
Author: Rajan Mandanka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-01 20:40:12

Gensim.utils.deaccent (text) from Gensim-topic modelling for humans :

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

Innym rozwiązaniem jest unidecode .

Sugerowane rozwiązanie z unicodedata zazwyczaj usuwa akcenty tylko w niektórych znakach (np. zamienia 'ł' na '', a nie na 'l').

 6
Author: Piotr Migdal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 00:27:58

Niektóre języki mają znaki diakrytyczne łączące jako litery językowe i znaki diakrytyczne akcentu, aby określić akcent.

Myślę, że bezpieczniej jest wyraźnie określić, jakie znaki diakrytyczne chcesz usunąć:

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))
 1
Author: sirex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-24 11:34:02