2009-08-24 9 views
54

tengo el siguiente código:¿Cómo hago que str.translate funcione con cadenas 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) 

Qué funciona muy bien para las cadenas no Unicode:

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

Pero falla por cadenas 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 

no puedo hacer ninguna sentido del párrafo sobre "objetos Unicode" en el Python 2.6.2 docs para el método str.translate().

¿Cómo puedo hacer este trabajo para cadenas Unicode?

+0

aquí hay un [ejemplo que elimina la puntuación Unicode de una cadena usando el método 'unicode.translate()'] (http://stackoverflow.com/a/11066687/4279). – jfs

+0

mejor uso 'cadena de importación; string.punctuation' en lugar de hardcoding 'not_letters_or_digits' en código real. Lo entiendo aquí, prefieres ser explícito. –

Respuesta

50

La versión Unicode de la traducción requiere una asignación de los ordinales de Unicode (que puede recuperar para un solo carácter con ord) a los ordinales de Unicode. Si desea eliminar caracteres, asigne un mapa al None.

que cambió su función de construir un diccionario mapear el ordinal de cada personaje al ordinal de lo que desea traducir a:

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__' 

edición: Resulta que la asignación de traducción debe asignar a partir el ordinal de Unicode (a través de ord) a otro ordinal Unicode, una cadena Unicode o None (para eliminar). Por lo tanto, he cambiado el valor predeterminado para translate_to para que sea un literal Unicode. Por ejemplo:

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

¡Gracias! (Una decisión de diseño tan tonta como para tener una función con un nombre idéntico que funciona de manera diferente.) – Sabuncu

+1

Además, si no desea definir manualmente los caracteres de puntuación: cadena de importación; translate_table = {ord (unicode (c)) para c en string.punctuation} Nota: Esto no traducirá todos los caracteres de puntuación unicode especiales (hay toneladas ...) – dpb

+0

su 'not_letters_or_digits' le falta '$' y 'Y'. Permítanme sugerir el uso de 'string.punctuation' en lugar de hardcoding del conjunto o caracteres –

5

me ocurrió la siguiente combinación de mi función original y la versión Mike 's que trabaja con cadenas Unicode y ASCII:

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) 

actualización: "coaccionado" translate_to a Unicode para el Unicode translate_table. Gracias Mike.

+0

Le sugiero que fuerce la traducción_to en Unicode para la versión Unicode, de lo contrario la llamada de traducción se volverá loca si le pasa una cadena Unicode, y" normal " " cuerda. –

+0

Esto parece algo que debería ser parte del lenguaje. +1 – bukzor

4

Para un simple truco que funciona en ambos objetos str y Unicode, convertir la tabla de traducción a unicode antes de ejecutar translate():

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) 

El truco aquí es que va a convertir implícitamente todos los objetos str para unicode, lanzando errores si to_translate contiene caracteres no ASCII.

0

En lugar de tener que especificar todos los caracteres que necesitan ser reemplazados, también se puede ver que a la inversa y, en cambio, especifique sólo los caracteres válidos, así:

import re 

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

    return result 

Esto funciona con unicode así como las cadenas regulares, y conserva el tipo (si el replacement_character y el source son del mismo tipo, obviamente).

7

En esta versión se puede hacer relativamente cartas de uno a otro

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

he encontrado que, cuando en Python 2.7, con el tipo str, podría escribir

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

mientras que con el tipo unicode que diría

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

en Python 3.6 que iba a escribir

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

tal vez esto es útil.

Cuestiones relacionadas