Pregunta

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

¿Fue útil?

Solución

Unidecode es la respuesta correcta para esto. Se transcribe cualquier cadena Unicode en 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'

Otros consejos

¿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 en letras griegas, también:

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

El categoría carácter "Mn" significa Nonspacing_Mark, que es similar a unicodedata.combining en la respuesta de MiniQuark (yo no pienso en unicodedata.combining, pero es probablemente la mejor solución, porque es más explícito).

Y tener en cuenta, estas manipulaciones pueden alterar significativamente el significado del texto. Acentos, diéresis, 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 (eliminación de los acentos) podría manejarse mejor que dejar caer los caracteres no ASCII, ya que esto se producirá un error en algunos idiomas (griego, por ejemplo) . La mejor solución sería probablemente para eliminar explícitamente los caracteres Unicode que están etiquetadas como siendo diacríticos.

Editar : este es 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 personaje c se puede combinar con el carácter anterior, es decir principalmente si es un diacrítica.

Editar 2 : remove_accents espera un Unicode cadena y no una cadena de bytes. Si usted tiene una cadena de bytes, entonces debe decodificar en una cadena Unicode como esto:

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)

Actualmente trabajo en el proyecto pitón compatible con 2.6, 2.7 y 3.4 y tengo que crear ID de entradas de usuarios libres.

Gracias a ti, he creado esta función que funciona de maravilla.

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 sólo se encarga de acentos, pero también "golpes" (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 que puedo pensar (y que ha sido mencionado por Alexis en un comentario en esta página), aunque no creo que sea muy elegante de hecho.

Todavía hay cartas especiales que no son manejados por esta, tales como letras transformadas e invertidos, ya que su nombre Unicode no contiene 'CON'. Depende de lo que quiere hacer de todos modos. a veces necesitaba acento que elimina para el logro de un orden de diccionario.

En respuesta a la respuesta de @MiniQuark:

Estaba intentando leer un archivo csv que era mitad francés (que contenía acentos) y también algunas cadenas que eventualmente se convertirían en números enteros y flotantes.Como prueba, creé un test.txt archivo que se veía así:

Montreal, über, 12.89, Mère, Françoise, noël, 889

Tuve que incluir líneas 2 y 3 para que funcione (que encontré en un ticket 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 datos unicode normalmente elimina los acentos sólo en algunos caracteres (p. ej.da vueltas 'ł' en '', en lugar de en 'l').

Algunas lenguas han combinar diacríticos como cartas de lenguaje y signos diacríticos del acento para especificar acento.

Creo que es más seguro especificar explícitamente lo diactrics desea despojar a:

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))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top