2009-08-24 5 views
54

je le code suivant:Comment est-ce que str.translate fonctionne avec les chaînes 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) 

Ce qui fonctionne très bien pour les chaînes non-unicode:

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

Mais échoue pour les chaînes 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 

Je ne peux rien sens du paragraphe sur "Objets Unicode" dans le Python 2.6.2 docs pour la méthode str.translate().

Comment faire pour que cela fonctionne pour les chaînes Unicode?

+0

Voici un exemple qui supprime la ponctuation Unicode d'une chaîne à l'aide de la méthode 'unicode.translate()' (http://stackoverflow.com/a/11066687/4279). – jfs

+0

mieux utiliser 'chaîne d'importation; string.punctuation' au lieu de hardcoding 'not_letters_or_digits' en code réel. Je comprends que vous préférez être explicite. –

Répondre

50

La version Unicode de translate nécessite un mappage à partir des ordinaux Unicode (que vous pouvez récupérer pour un seul caractère avec ord) en unités Unicode. Si vous souhaitez supprimer des caractères, vous mappez sur None.

j'ai changé votre fonction pour construire un dict cartographier l'ordinal de chaque caractère à l'ordinal de ce que vous voulez traduire:

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

modifier: Il se trouve que le mappage de traduction doit carte de l'ordinal Unicode (via ord) à un autre ordinal Unicode, une chaîne Unicode ou None (à supprimer). J'ai donc changé la valeur par défaut pour translate_to pour être un littéral Unicode. Par exemple:

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

Merci! (Une telle décision de conception stupide d'avoir une fonction nommée identique qui fonctionne différemment.) – Sabuncu

+1

Aussi, si vous ne voulez pas définir manuellement les caractères de ponctuation: chaîne d'importation; translate_table = {ord (unicode (c)) pour c dans string.punctuation} Note: Ceci ne traduira pas tous les caractères spéciaux de ponctuation unicode (il y en a des tonnes ...) – dpb

+0

votre 'not_letters_or_digits' manque '$' et '&'. Laissez-moi vous suggérer d'utiliser 'string.punctuation' au lieu de coder en dur l'ensemble ou les caractères –

5

je suis venu avec la combinaison suivante de ma fonction originale et la version de Mike qui fonctionne avec des chaînes Unicode et 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) 

Mise à jour: "sous la contrainte" translate_to à Unicode pour l'Unicode translate_table. Merci Mike.

+0

Je suggérerais que vous comptiez translate_to en Unicode pour la version Unicode, sinon l'appel translate flippera si vous lui passez une chaîne Unicode, et" normal " chaîne. –

+0

Cela semble être quelque chose qui devrait faire partie de la langue. +1 – bukzor

4

Pour un simple hack qui fonctionne sur les deux objets str et unicode, convertir la table de traduction en unicode avant d'exécuter traduire():

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) 

Le problème ici est qu'il convertira implicitement tous les objets str à unicode, en lançant des erreurs si to_translate contient des caractères non-ascii.

0

Au lieu d'avoir à spécifier tous les caractères qui doivent être remplacés, vous pouvez aussi voir l'autre sens, et, il faut préciser que les caractères valides, comme ceci:

import re 

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

    return result 

Cela fonctionne avec Unicode ainsi que des chaînes régulières, et conserve le type (si les deux replacement_character et source sont du même type, évidemment).

7

Dans cette version, vous pouvez relativement faire ses lettres à d'autres

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

Je trouve que là où en python 2.7, avec le type str, vous écririez

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

alors avec le type unicode vous diriez

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

En Python 3.6 vous écririez

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

cela est peut-être utile.

Questions connexes