2009-09-05 12 views
14

Ho una stringa unicode con caratteri latini accentati, ad es.latin-1 in ascii

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

voglio convertirlo in ASCII cioe 'Wikipedia, Le projet dencyclopedie', in modo che tutti acuta/accento, cediglia ecc dovrebbe ottenere rimosso

Qual è il modo più veloce per farlo, in quanto doveva essere fatto per la corrispondenza un elenco a discesa lunga completamento automatico

Conclusione: Come uno i miei criteri è la velocità, Lennart del 'registrare il proprio gestore degli errori per la codifica unicode/decodifica' dà miglior risultato (vedi risposta di Alex), velocità la differenza aumenta ulteriormente come sempre più c hars sono latini.

Ecco la tabella di traduzione che sto usando, gestore degli errori anche modificato in quanto ha bisogno di prendersi cura di tutta una serie di non-codificato char da 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') 

Perché torno error.start + 1:

errore oggetto restituito può essere più caratteri e convertiamo solo il primo di questi ad es. se aggiungo print error.start, error.end all'errore uscita gestore è

¼ éíñ§ÐÌëÑ » ¼ ö ® © ’ 
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 ' 

così in seconda linea otteniamo caratteri da 2-10 ma abbiamo convertire solo 2 °, quindi, tornare 3 come continuare punto, se torniamo uscita error.end è

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

Come possiamo vedere la porzione 2-10 è stata sostituita da un singolo carattere. fuori rotta sarebbe più veloce codificare un'intera gamma in un colpo solo e restituire error.end, ma a fini dimostrativi l'ho mantenuta semplice.

vedere http://docs.python.org/library/codecs.html#codecs.register_error per maggiori dettagli

+0

Sono certo che ne sei a conoscenza, ma fai attenzione a non mostrare queste stringhe ascii-fied all'utente. Il significato di una parola può cambiare totalmente quando cambi le lettere più o meno a caso (facendo 'ö' in 'o' e così via). – unwind

+0

sì, questo non è per la visualizzazione ma per la digitazione, abbiamo una tastiera sullo schermo con lettere ascii problema è come l'utente digiterà é o õ, quindi se i tipi e, dovrebbe corrispondere alla stringa avente e, é, ê ecc –

+0

I don ' t comprendere la tua sostituzione di 'error.start + 1' per' error.end'.Puoi spiegare per favore? Entrambi sembrano funzionare allo stesso modo per me. – gorus

risposta

15

Così qui sono tre approcci, più o meno come dato o suggerito in altre risposte:

# -*- 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() 

Esegui come principale, questo emette:

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 

che mostra chiaramente che l'approccio unicodedata-based , mentre ha la comodità di non aver bisogno di una mappa di traduzione xtd, non è possibile tradurre tutti i caratteri correttamente in modo automatico (funziona per lettere accentate ma non per l'inverso-apostrofo), quindi avrebbe bisogno anche di un passaggio ausiliario per trattare esplicitamente wi quelli (senza dubbio prima di quello che è ora il suo corpo).

Anche le prestazioni sono interessanti. Sul mio computer portatile con Mac OS X 10.5 e il sistema di Python 2.5, abbastanza ripetibile:

$ 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 è sorprendentemente lenta (rispetto agli altri approcci). Credo che il problema sia che il dtt viene esaminato per ogni personaggio nel caso translate (e la maggior parte non ci sono), ma solo per quei pochi caratteri che SONO lì con l'approccio asciify.

Così, per completezza, ecco approccio "unicodedata rinforzato-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') 

Questo dà l'uscita a destra, MA:

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

... la velocità non è tutto ciò che serve più . Quindi, se la velocità conta, è senza dubbio la pena di creare un comando di traduzione completo xtd e utilizzare l'approccio asciify. Quando pochi microsecondi aggiuntivi per traduzione non sono un grosso problema, si potrebbe prendere in considerazione l'approccio bu semplicemente per la sua convenienza (richiede solo una traduzione per, sperabilmente pochi, caratteri speciali che non si traducono correttamente con l'idea di unicodedata sottostante).

+0

Grazie per il riepilogo e il loro timing :) –

+0

c'è un motivo per fare 'ord' durante la creazione di dict e ottenere di nuovo ordinale mentre si asciuga? –

+0

@Anurag, la ragione per cui il dict è così da renderlo immediatamente utilizzabile in '.translate' -' asciify' ovviamente non ne ha bisogno. La semplificazione riduce i tempi, approssimativamente, da 7,5 a 7,3 usec. –

0

Senza misurare, mi aspetterei che il metodo .translate di stringhe Unicode è la soluzione più veloce. Dovresti sicuramente fare le tue misurazioni, però.

1

Maketrans (e tradurre) poi convertire in 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 

(da here)

+0

Ma questo li convertirà in entità carattere xml. Non è quello che ha chiesto. –

+0

Non capisco la prima riga della soluzione "Maketrans (e traduci) quindi converti in ascii:" perché è necessario e non lo usi da nessuna parte nel codice? –

+0

@Lennart Regebro: Quindi li codifica in ASCII. @Anurag Uniyal: ha voluto sostituire per es. 'é' con 'e' che una semplice conversione non farebbe per lui. Questo è il motivo per cui è necessario maketrans. Lo snippet di codice che ho copiato qui mostra solo la conversione Unicode-> ASCII. –

8

Il modo "corretto" per farlo è quello di registrare il proprio gestore degli errori per Unicode codifica/decodifica, e in quel gestore degli errori di fornire le sostituzioni da e a e ed o recarsi, ecc

Come così:

# -*- 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') 

È inoltre possibile trovare qualcosa nella libreria IBM ICU e le sue associazioni Python PyICU, tuttavia, potrebbe essere meno lavoro.

+0

+1: vorrei aggiungere un po 'più di controllo sull'input per la funzione asciify, ma penso che questo sia anche un riferimento molto veloce e valido per la gestione personalizzata degli errori nella codifica Unicode. –

+0

Sono d'accordo che questa è l'implementazione corretta. Forse qualcuno può suggerire una mappatura completa per un uso generico. –

+0

+1 per la risposta corretta ma penso che sceglierei la risposta di Alex per essere completa e che includesse i tempi. –

8

Il modulo impressionante unidecode lo fa per voi:

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

sì sembra essere simile ma esteso –

0

pacchetto unihandecode è

traslitterazioni US-ASCII di testo Unicode.
una versione migliorata di Python unidecode, ovvero la porta Python del modulo Testo :: Unidecode Perl di Sean M. Burke.

pip install Unihandecode 

poi nel python

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

stampe Wikipedia, le projet d'encyclopedie.