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

Resuelto MiniQuark asked hace 15 años • 14 respuestas

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

Encontré en la web una manera elegante de hacer esto (en Java):

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

¿Necesito instalar una biblioteca como pyICU o es posible solo con la biblioteca estándar de Python? ¿Y qué pasa con 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.

MiniQuark avatar Feb 06 '09 04:02 MiniQuark
Aceptado

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

Ejemplo:

>>> from unidecode import unidecode
>>> unidecode('kožušček')
'kozuscek'
>>> unidecode('北亰')
'Bei Jing '
>>> unidecode('François')
'Francois'
Christian Oudard avatar Apr 13 '2010 21:04 Christian Oudard

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 también funciona con 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.combining en la respuesta de MiniQuark (no pensé en unicodedata.combining, 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. Los acentos, diéresis, etc. no son "decoración".

oefe avatar Feb 05 '2009 22:02 oefe

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 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, porque esto fallará en algunos idiomas (griego, por ejemplo). La mejor solución probablemente sería eliminar explícitamente los caracteres Unicode etiquetados como signos diacríticos.

Editar : esto funciona:

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 cse puede combinar con el carácter anterior, es decir, principalmente si es un signo diacrítico.

Edición 2 : remove_accentsespera 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)
MiniQuark avatar Feb 05 '2009 21:02 MiniQuark