Подтвердить что ты не робот

Как заставить str.translate работать с строками 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)

Что отлично работает для строк, отличных от unicode:

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

Но сбой для строк 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

Я не могу понять смысл абзаца "Объекты Unicode" в Python 2.6.2 docs для str.translate().

Как это сделать для строк Unicode?

4b9b3361

Ответ 1

Для версии Unicode для перевода требуется сопоставление ординалов Unicode (которые вы можете получить для одного символа с ord) в ординалах Unicode. Если вы хотите удалить символы, вы переходите к None.

Я изменил вашу функцию, чтобы построить диктофон, сопоставляющий порядковый номер каждого символа с порядковым номером того, что вы хотите перевести:

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: Оказывается, что отображение перевода должно отображаться из порядкового номера Юникода (через ord) в любой другой порядковый номер Unicode, строку Unicode или None (для удаления). Таким образом, я изменил значение по умолчанию для translate_to как литерал Юникода. Например:

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

Ответ 2

В этой версии вы можете сделать одну букву с другим

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)

Ответ 3

Я придумал следующую комбинацию моей оригинальной функции и Mike, которая работает с строками Unicode и 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)

Обновить: "принудительно" translate_to в unicode для юникода translate_table. Спасибо Майку.

Ответ 4

Для простого взлома, который будет работать как для объектов str, так и для unicode, преобразуйте таблицу перевода в unicode перед запуском 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)

Ловушка здесь заключается в том, что он будет неявно преобразовывать все объекты str в unicode, бросание ошибок, если to_translate содержит символы не-ascii.

Ответ 5

Вместо того, чтобы указывать все символы, которые необходимо заменить, вы также можете просматривать его наоборот и вместо этого указывать только допустимые символы, например:

import re

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

    return result

Это работает как с unicode, так и с обычными строками и сохраняет тип (если оба replacement_character и source имеют одинаковый тип, очевидно).

Ответ 6

Я обнаружил, что где в python 2.7, с типом str, вы должны написать

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

тогда как с типом unicode вы скажете

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

В python 3.6 вы напишете

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

Возможно, это полезно.