¿Cuál es la mejor manera de eliminar los acentos en una cadena Unicode de Python?

Tengo una cadena Unicode en Python, y me gustaría eliminar todos los acentos (diacríticos).

Encontré en la Web una forma elegante de hacer esto en Java:

  1. convertir la cadena Unicode a su forma normalizada larga (con un carácter separado para letras y signos diacríticos)
  2. eliminar todos los caracteres cuyo tipo de Unicode es “diacrítico”.

¿Necesito instalar una biblioteca como pyICU o esto es posible solo con la biblioteca estándar de python? ¿Y qué hay de python 3?

Nota importante: me gustaría evitar el código con una asignación explícita de caracteres acentuados a su contraparte no acentuada.

Unidecode es la respuesta correcta para esto. Transcribe cualquier cadena de Unicode a la representación más cercana posible en texto ASCII.

Ejemplo:

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' 

Qué tal esto:

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

Esto funciona también en letras griegas:

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

La categoría de caracteres “Mn” significa Nonspacing_Mark , que es similar a unicodedata.combinando en la respuesta de MiniQuark (no pensé en unicodedata.combinando, pero probablemente sea la mejor solución, porque es más explícita).

Y tenga en cuenta que estas manipulaciones pueden alterar significativamente el significado del texto. Acentos, Umlauts, etc. no son “decoración”.

Acabo de encontrar esta respuesta en la Web:

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

Funciona bien (para el francés, por ejemplo), pero creo que el segundo paso (eliminar los acentos) podría manejarse mejor que eliminar los caracteres que no son ASCII, ya que esto fallará en algunos idiomas (griego, por ejemplo). La mejor solución probablemente sería eliminar explícitamente los caracteres Unicode que están etiquetados como diacríticos.

Edición : esto hace el truco:

 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) devolverá verdadero si el carácter c puede combinarse con el carácter anterior, es decir, principalmente si es un diacrítico.

Edición 2 : remove_accents espera una cadena Unicode , no una cadena de bytes. Si tiene una cadena de bytes, debe decodificarla en una cadena Unicode como esta:

 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) 

En realidad, trabajo en Python 2.6, 2.7 y 3.4 compatible con el proyecto y tengo que crear ID a partir de entradas de usuario gratuitas.

Gracias a ti, he creado esta función que hace maravillas.

 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 

resultado:

 text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889") >>> 'montreal_uber_1289_mere_francoise_noel_889' 

Esto no solo maneja los acentos, sino también los “trazos” (como en ø, etc.):

 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) 

Esta es la forma más elegante en la que puedo pensar (y ha sido mencionada por alexis en un comentario en esta página), aunque no creo que sea muy elegante.

Todavía hay letras especiales que no se manejan por esto, como las letras giradas e invertidas, ya que su nombre de Unicode no contiene “CON”. Depende de lo que quieras hacer de todos modos. Algunas veces necesitaba acentos para lograr el orden del diccionario.

En respuesta a la respuesta de @ MiniQuark:

Intentaba leer en un archivo csv que era mitad francés (con acentos) y también algunas cadenas que eventualmente se convertirían en números enteros y flotantes. Como prueba, creé un archivo test.txt que tenía este aspecto:

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

Tuve que incluir las líneas 2 y 3 para que funcionara (lo que encontré en un boleto de python), así como incorporar el comentario de @ 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) 

El resultado:

 Montreal uber 12.89 Mere Francoise noel 889 

(Nota: Estoy en Mac OS X 10.8.4 y uso Python 2.7.3)

gensim.utils.deaccent (texto) de Gensim – modelado de temas para humanos :

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

Otra solución es unidecode .

No es que la solución sugerida con unicodedata generalmente elimine los acentos en algunos caracteres (por ejemplo, convierte 'ł' en '' , en lugar de 'l' ).

Algunos idiomas combinan los signos diacríticos con letras del idioma y los signos diacríticos de acento para especificar el acento.

Creo que es más seguro especificar explícitamente qué diactricas quieres despojar:

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