latin-1 a ascii

Tengo una cadena Unicode con caracteres latinos acentuados, por ejemplo

n=unicode('Wikipédia, le projet d'encyclopédie','utf-8') 

Quiero convertirlo en simple ascii, es decir, ‘Wikipedia, le projet dencyclopedie’, por lo que debería eliminarse todo lo agudo / acento, cedilla, etc.

¿Cuál es la forma más rápida de hacerlo, ya que era necesario hacerlo para hacer coincidir una larga lista desplegable de autocompletado?

Conclusión: Como uno de mis criterios es la velocidad, el “mejor registro de Lennart para su propio controlador de errores para encoding / deencoding de Unicode” (ver la respuesta de Alex) aumenta aún más a medida que más y más caracteres son latinos.

Aquí está la tabla de traducción que estoy usando, también el controlador de errores modificado, ya que debe ocuparse de toda la gama de caracteres no codificados de error.start a error.end

 # -*- coding: utf-8 -*- import codecs """ This is more of visual translation also avoiding multiple char translation eg £ may be written as {pound} """ latin_dict = { u"¡": u"!", u"¢": u"c", u"£": u"L", u"¤": u"o", u"¥": u"Y", u"¦": u"|", u"§": u"S", u"¨": u"`", u"©": u"c", u"ª": u"a", u"«": u"<>", u"¼": u"1/4", u"½": u"1/2", u"¾": u"3/4", u"¿": u"?", u"À": u"A", u"Á": u"A", u"Â": u"A", u"Ã": u"A", u"Ä": u"A", u"Å": u"A", u"Æ": u"Ae", u"Ç": u"C", u"È": u"E", u"É": u"E", u"Ê": u"E", u"Ë": u"E", u"Ì": u"I", u"Í": u"I", u"Î": u"I", u"Ï": u"I", u"Ð": u"D", u"Ñ": u"N", u"Ò": u"O", u"Ó": u"O", u"Ô": u"O", u"Õ": u"O", u"Ö": u"O", u"×": u"*", u"Ø": u"O", u"Ù": u"U", u"Ú": u"U", u"Û": u"U", u"Ü": u"U", u"Ý": u"Y", u"Þ": u"p", u"ß": u"b", u"à": u"a", u"á": u"a", u"â": u"a", u"ã": u"a", u"ä": u"a", u"å": u"a", u"æ": u"ae", u"ç": u"c", u"è": u"e", u"é": u"e", u"ê": u"e", u"ë": u"e", u"ì": u"i", u"í": u"i", u"î": u"i", u"ï": u"i", u"ð": u"d", u"ñ": u"n", u"ò": u"o", u"ó": u"o", u"ô": u"o", u"õ": u"o", u"ö": u"o", u"÷": u"/", u"ø": u"o", u"ù": u"u", u"ú": u"u", u"û": u"u", u"ü": u"u", u"ý": u"y", u"þ": u"p", u"ÿ": u"y", u"'":u"'"} def latin2ascii(error): """ error is protion of text from start to end, we just convert first hence return error.start+1 instead of error.end """ return latin_dict[error.object[error.start]], error.start+1 codecs.register_error('latin2ascii', latin2ascii) if __name__ == "__main__": x = u"¼ éíñ§ÐÌëÑ » ¼ ö ® © '" print x print x.encode('ascii', 'latin2ascii') 

Por qué vuelvo error.start + 1 :

el objeto de error devuelto puede tener varios caracteres, y solo convertimos el primero de ellos, por ejemplo, si agrego print error.start, error.end a la salida del controlador de errores es

 ¼ éíñ§ÐÌëÑ » ¼ ö ® © ' 0 1 2 10 3 10 4 10 5 10 6 10 7 10 8 10 9 10 11 12 13 14 15 16 17 18 19 20 21 22 1/4 einSDIeN >> 1/4 o R c ' 

así que en la segunda línea obtenemos los caracteres del 2 al 10, pero convertimos solo el 2 por lo tanto, devolvemos 3 como punto de continuación, si devolvemos el error.

 ¼ éíñ§ÐÌëÑ » ¼ ö ® © ' 0 1 2 10 11 12 13 14 15 16 17 18 19 20 21 22 1/4 e >> 1/4 o R c ' 

Como podemos ver, 2-10 porciones han sido reemplazadas por una sola charla. por supuesto, sería más rápido codificar todo el rango de una sola vez y devolver el error.end, pero para fines de demostración lo he mantenido simple.

vea http://docs.python.org/library/codecs.html#codecs.register_error para más detalles

Así que aquí hay tres enfoques, más o menos según lo dado o sugerido en otras respuestas:

 # -*- coding: utf-8 -*- import codecs import unicodedata x = u"Wikipédia, le projet d'encyclopédie" xtd = {ord(u'''): u"'", ord(u'é'): u'e', } def asciify(error): return xtd[ord(error.object[error.start])], error.end codecs.register_error('asciify', asciify) def ae(): return x.encode('ascii', 'asciify') def ud(): return unicodedata.normalize('NFKD', x).encode('ASCII', 'ignore') def tr(): return x.translate(xtd) if __name__ == '__main__': print 'or:', x print 'ae:', ae() print 'ud:', ud() print 'tr:', tr() 

Ejecutar como principal, esto emite:

 or: Wikipédia, le projet d'encyclopédie ae: Wikipedia, le projet d'encyclopedie ud: Wikipedia, le projet dencyclopedie tr: Wikipedia, le projet d'encyclopedie 

mostrando claramente que el enfoque basado en unicodedata, si bien tiene la conveniencia de no necesitar un mapa de traducción xtd , no puede traducir todos los caracteres correctamente de manera automática (funciona para letras acentuadas pero no para el apóstrofe inverso), por lo que también necesitaría algún paso auxiliar para tratar explícitamente con esos (sin duda antes de lo que es ahora su cuerpo).

El rendimiento también es interesante. En mi computadora portátil con Mac OS X 10.5 y el sistema Python 2.5, es bastante repetible:

 $ python -mtimeit -s'import a' 'a.ae()' 100000 loops, best of 3: 7.5 usec per loop $ python -mtimeit -s'import a' 'a.ud()' 100000 loops, best of 3: 3.66 usec per loop $ python -mtimeit -s'import a' 'a.tr()' 10000 loops, best of 3: 21.4 usec per loop 

translate es sorprendentemente lenta (en relación con los otros enfoques). Creo que el problema es que el dictamen se analiza para cada personaje en el caso de translate (y la mayoría no está ahí), pero solo para aquellos pocos personajes que ESTÁN allí con el enfoque asciify .

Así que, para completar, aquí está el enfoque de “unicodedata reforzado”:

 specstd = {ord(u'''): u"'", } def specials(error): return specstd.get(ord(error.object[error.start]), u''), error.end codecs.register_error('specials', specials) def bu(): return unicodedata.normalize('NFKD', x).encode('ASCII', 'specials') 

esto da la salida correcta, PERO:

 $ python -mtimeit -s'import a' 'a.bu()' 100000 loops, best of 3: 10.7 usec per loop 

… la velocidad ya no es tan buena. Por lo tanto, si la velocidad importa, sin duda merece la pena hacer un xtd traducción xtd completo y usar el enfoque asciify . Cuando unos pocos microsegundos adicionales por traducción no son un gran problema, uno debería considerar el enfoque de bu simplemente por su conveniencia (solo necesita un dictado de traducción para, con suerte, pocos caracteres especiales que no se traduzcan correctamente con la idea subyacente de unicodedata).

El increíble módulo Unidecode hace esto por ti:

 >>> import unidecode >>> n = unicode('Wikipédia, le projet d'encyclopédie','utf-8') >>> unidecode.unidecode(n) "Wikipedia, le projet d'encyclopedie" 

La forma “correcta” de hacer esto es registrar su propio controlador de errores para la encoding / deencoding de Unicode, y en ese controlador de errores proporcionar los reemplazos de è a e y ö a o, etc.

Al igual que:

 # -*- coding: UTF-8 -*- import codecs map = {u'é': u'e', u''': u"'", # ETC } def asciify(error): return map[error.object[error.start]], error.end codecs.register_error('asciify', asciify) test = u'Wikipédia, le projet d'encyclopédie' print test.encode('ascii', 'asciify') 

También puede encontrar algo en la biblioteca de la ICU de IBM y es PyICU , que enlaza Python, podría ser menos laborioso .

Maketrans (y traduce) luego se convierte a ascii:

 intab = u'áéí' # extend as needed outtab = u'aei' # as well as this one table = maketrans(intab, outtab) text = translate(u"Wikipédia, le projet d'encyclopédie", table) try: temp = unicode(text, "utf-8") fixed = unicodedata.normalize('NFKD', temp).encode('ASCII', action) return fixed except Exception, errorInfo: print errorInfo print "Unable to convert the Unicode characters to xml character entities" raise errorInfo 

(desde aquí )

Sin medir, esperaría que el método .translate de las cadenas Unicode sea la solución más rápida. Sin embargo, definitivamente debes hacer tus propias medidas.

Paquete unihandecode es

US-ASCII transliteraciones de texto Unicode.
una versión mejorada de Python unidecode, que es el puerto Python del módulo Text :: Unidecode Perl de Sean M. Burke.

 pip install Unihandecode 

entonces en python

 import unihandecode print(unihandecode.unidecode(u'Wikipédia, le projet d'encyclopédie')) 

Imprime Wikipedia, le projet d'encyclopedie .