2009-08-24 14 views
54

Ho il seguente codice:Come posso ottenere che str.translate funzioni con le stringhe Unicode?

import string 
def translate_non_alphanumerics(to_translate, translate_to='_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = string.maketrans(not_letters_or_digits, 
             translate_to 
             *len(not_letters_or_digits)) 
    return to_translate.translate(translate_table) 

Quali grandi opere per le stringhe non Unicode:

>>> translate_non_alphanumerics('<foo>!') 
'_foo__' 

Ma non riesce per le stringhe Unicode:

>>> translate_non_alphanumerics(u'<foo>!') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in translate_non_alphanumerics 
TypeError: character mapping must return integer, None or unicode 

non posso fare qualsiasi senso del paragrafo su "Oggetti Unicode" nel Python 2.6.2 docs per il metodo str.translate().

Come faccio a funzionare per le stringhe Unicode?

+0

ecco un [esempio che rimuove la punteggiatura Unicode da una stringa usando il metodo 'unicode.translate()' (http://stackoverflow.com/a/11066687/4279). – jfs

+0

usare meglio 'stringa di importazione; string.punctuation' invece di hardcoding 'not_letters_or_digits' in codice reale. Capisco che qui preferiresti essere esplicito. –

risposta

50

La versione di traduzione Unicode richiede un'associazione da ordinali Unicode (che è possibile recuperare per un singolo carattere con ord) in ordinali Unicode. Se si desidera eliminare i caratteri, si esegue il mapping su None.

ho cambiato la funzione di costruire un dict mappatura del ordinale di ogni personaggio per il numero ordinale di ciò che si vuole tradurre in:

def translate_non_alphanumerics(to_translate, translate_to=u'_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = dict((ord(char), translate_to) for char in not_letters_or_digits) 
    return to_translate.translate(translate_table) 

>>> translate_non_alphanumerics(u'<foo>!') 
u'_foo__' 

edit: Si scopre che la mappatura traduzione deve mappa da l'ordinale Unicode (via ord) a un altro ordinale Unicode, a una stringa Unicode oa Nessuno (da eliminare). Ho quindi modificato il valore predefinito per translate_to in modo che fosse un valore letterale Unicode. Per esempio:

>>> translate_non_alphanumerics(u'<foo>!', u'bad') 
u'badfoobadbad' 
+9

Grazie! (Tale decisione stupida sul design di avere una funzione con nome identico che opera in modo diverso.) – Sabuncu

+1

Inoltre, se non si desidera definire manualmente i caratteri di punteggiatura: stringa di importazione; translate_table = {ord (unicode (c)) per c in string.punctuation} Nota: questo non tradurrà tutti i caratteri speciali di punteggiatura Unicode (ci sono tonnellate ...) – dpb

+0

il tuo 'not_letters_or_digits' manca '$' e '&'. Consentitemi di usare 'string.punctuation' invece di codificare il set oi caratteri –

5

Sono venuto con la seguente combinazione della mia funzione originale e la versione Mike s' che funziona con Unicode e ASCII stringhe:

def translate_non_alphanumerics(to_translate, translate_to=u'_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    if isinstance(to_translate, unicode): 
     translate_table = dict((ord(char), unicode(translate_to)) 
           for char in not_letters_or_digits) 
    else: 
     assert isinstance(to_translate, str) 
     translate_table = string.maketrans(not_letters_or_digits, 
              translate_to 
               *len(not_letters_or_digits)) 
    return to_translate.translate(translate_table) 

Aggiornamento: "costretto" translate_to a unicode per l'unicode translate_table. Grazie Mike.

+0

Suggerirei di forzare il valore tradotto_to in Unicode per la versione Unicode, altrimenti la chiamata di traduzione diventerà strana se si passa una stringa Unicode e" normale " "stringa. –

+0

Questo sembra qualcosa che dovrebbe essere parte della lingua. +1 – bukzor

4

Per un semplice hack che funziona su entrambi gli oggetti str e unicode, convertire la tabella di conversione a Unicode prima esecuzione tradurre():

import string 
def translate_non_alphanumerics(to_translate, translate_to='_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = string.maketrans(not_letters_or_digits, 
             translate_to 
             *len(not_letters_or_digits)) 
    translate_table = translate_table.decode("latin-1") 
    return to_translate.translate(translate_table) 

La cattura è che sarà convertire implicitamente tutti gli oggetti str per unicode, errori di lancio se to_translate contiene caratteri non ascii.

0

Invece di dover specificare tutti i personaggi che hanno bisogno di essere sostituiti, si potrebbe anche vederlo il contrario e, invece, specificare solo i caratteri validi, in questo modo:

import re 

def replace_non_alphanumerics(source, replacement_character='_'): 
    result = re.sub("[^_a-zA-Z0-9]", replacement_character, source) 

    return result 

Questo funziona con unicode e stringhe regolari e mantiene il tipo (se sia lo replacement_character che lo source sono dello stesso tipo, ovviamente).

7

In questa versione è possibile relativamente compiere le lettere ad altri

def trans(to_translate): 
    tabin = u'привет' 
    tabout = u'тевирп' 
    tabin = [ord(char) for char in tabin] 
    translate_table = dict(zip(tabin, tabout)) 
    return to_translate.translate(translate_table) 
0

ho scoperto che mentre in Python 2.7, con il tipo str, si può scrivere

import string 
table = string.maketrans("123", "abc") 
print "135".translate(table) 

mentre con il tipo unicode direste

table = {ord(s): unicode(d) for s, d in zip("123", "abc")} 
print u"135".translate(table) 

in Python 3.6 si può scrivere

table = {ord(s): d for s, d in zip("123", "abc")} 
print("135".translate(table)) 

forse questo è utile.

Problemi correlati