2009-09-05 22 views
14

Tengo una cadena Unicode con caracteres latinos acentuados, p.latin-1 a ascii

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

quiero convertirlo en ASCII es decir, 'Wikipedia, le projet dencyclopedie', por lo que todos aguda/acento, cedilla, etc. deben quedar eliminado

¿Cuál es la manera más rápida de hacer eso, ya que que había que hacer para hacer coincidir una lista desplegable a largo autocompletar

Conclusión: Como uno de mis criterios es la velocidad, Lennart de 'registrar su propio gestor de errores de codificación unicode/decodificación' da mejor resultado (véase la respuesta de Alex), velocidad la diferencia aumenta aún más a medida que aumenta la c hars son latinos

Aquí está la tabla de traducción que estoy usando, gestor de errores también modificado, ya que tiene que hacerse cargo de toda la gama de carbón no-codificado de error.start a error.end

# -*- coding: utf-8 -*- 
import codecs 

""" 
This is more of visual translation also avoiding multiple char translation 
e.g. £ 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"-", u"­": u"-", u"®": u"R", u"¯": u"-", 
u"°": u"o", u"±": u"+-", u"²": u"2", u"³": u"3", u"´": u"'", 
u"µ": u"u", u"¶": u"P", u"·": u".", u"¸": u",", u"¹": u"1", 
u"º": u"o", 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 ser de varios caracteres, por ejemplo, solo convertimos primero, por ejemplo si añado print error.start, error.end al error de salida manejador 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 ' 

por lo que en segunda línea tenemos caracteres de 2-10 pero convertir sólo segundo por lo tanto, volver 3 como punto de continuar, si volvemos salida error.end es

¼ éíñ§ÐÌëÑ » ¼ ö ® © ’ 
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, la parte 2-10 ha sido reemplazada por una sola char. fuera de curso, sería más rápido codificar todo el rango de una vez y devolver el error. Pero para fines de demostración lo he mantenido simple.

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

+0

Estoy seguro de que está al tanto, pero tenga cuidado de no mostrar estas cadenas de caracteres certificadas al usuario. El significado de una palabra puede cambiar totalmente cuando cambias las letras más o menos al azar (lo que hace que 'o' se convierta en 'o' y así sucesivamente). – unwind

+0

sí esto no es para mostrar sino para escribir, tenemos un teclado en pantalla con letras ascii problema es cómo el usuario tecleará é o õ, entonces si los tipos e, debe coincidir con cadena que tiene e, é, ê etc. –

+0

I Don ' Entiendo su sustitución de 'error.start + 1' por' error.end'.¿Puede usted explicar por favor? Ambos parecen funcionar igual para mí. – gorus

Respuesta

15

Así que aquí hay tres enfoques, más o menos como dados o sugeridos 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() 

Run como principal, este 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 forma automática (funciona para letras acentuadas pero no para el apóstrofo inverso), por lo que también necesitaría un paso auxiliar para tratar explícitamente con wi esos (sin duda antes de lo que ahora es su cuerpo).

El rendimiento también es interesante. En mi portátil con Mac OS X 10.5 y sistema de Python 2.5, 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 se examina el dict para cada personaje en el caso translate (y la mayoría no están allí), pero solo para esos pocos caracteres que están allí con el enfoque asciify.

Así está completo aquí está el enfoque "unicodedata reforzado-up":

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 no es tan bueno como tampoco . Por lo tanto, si la velocidad es importante, sin duda vale la pena hacer una traducción completa de xtd dict y utilizando el enfoque asciify. Cuando unos pocos microsegundos adicionales por traducción no son un gran problema, uno puede considerar el enfoque bu simplemente por su conveniencia (solo necesita un dict de traducción para, con suerte, pocos caracteres especiales que no se traducen correctamente con la idea unicodedata subyacente).

+0

Gracias por el resumen y cronometrarlos :) –

+0

¿Hay alguna razón detrás de hacer 'ord' mientras se crea el dict y volverse ordinal mientras se está asciificando? –

+0

@Anurag, la razón por la cual el dict es de esa manera es para que sea inmediatamente utilizable en '.translate' -' asciify' por supuesto no necesita eso. La simplificación reduce su tiempo, aproximadamente, de 7.5 a 7.3 usec. –

0

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

1

Maketrans (y traducir) a continuación, convertir 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 here)

+0

Pero esto los convertirá en entidades de caracteres xml. Eso no es lo que él pidió. –

+0

No entiendo la primera línea de la solución "Maketrans (y traducir) luego convierte a ascii:" ¿por qué es necesario y no lo usa en ningún lugar del código? –

+0

@Lennart Regebro: Luego los codifica en ASCII. @Anurag Uniyal: quería reemplazar, p. 'é' con 'e' que una simple conversión no haría por él. Es por eso que se necesita maketrans. El fragmento de código que copié aquí solo muestra la conversión de Unicode-> ASCII. –

8

La forma "correcta" de hacerlo es registrar su propio gestor de errores para Unicode de codificación/decodificación, y en ese gestor de errores proporcionar los reemplazos de e a e y O a S, etc.

así:

# -*- 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 IBM ICU y sus enlaces de Python PyICU, sin embargo, podría ser menos trabajo.

+0

+1: Solo añadiría algo más de comprobación en la entrada para la función asciify, pero creo que esta es también una referencia muy rápida y buena para el manejo personalizado de errores en la codificación Unicode. –

+0

Acepto que esta es la implementación correcta. Tal vez alguien pueda sugerir una asignación completa para uso general. –

+0

+1 para la respuesta correcta, pero creo que elegiría la respuesta de Alex por ser completa y eso incluía los tiempos. –

8

El módulo impresionante unidecode lo hace por usted:

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

sí, parece ser similar pero extensa –

0

paquete unihandecode es

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

pip install Unihandecode 

después en python

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

impresiones Wikipedia, le projet d'encyclopedie.