Декодирование, если оно не unicode
Я хочу, чтобы моя функция приняла аргумент, который может быть объектом unicode или строкой, кодированной utf-8. Внутри моей функции я хочу преобразовать аргумент в unicode. У меня есть что-то вроде этого:
def myfunction(text):
if not isinstance(text, unicode):
text = unicode(text, 'utf-8')
...
Можно ли избежать использования isinstance? Я искал что-то более дружелюбное для утки.
Во время моих экспериментов с расшифровкой я столкнулся с несколькими странными поведением Python. Например:
>>> u'hello'.decode('utf-8')
u'hello'
>>> u'cer\xf3n'.decode('utf-8')
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/lib/python2.6/encodings/utf_8.py", line 16, in decode
return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xf3' in po
sition 3: ordinal not in range(128)
или
>>> u'hello'.decode('utf-8')
u'hello' 12:11
>>> unicode(u'hello', 'utf-8')
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: decoding Unicode is not supported
Кстати. Я использую Python 2.6
Ответы
Ответ 1
Вы можете просто попробовать декодировать его с помощью кодека "utf-8", и если это не сработает, верните объект.
def myfunction(text):
try:
text = unicode(text, 'utf-8')
except TypeError:
return text
print(myfunction(u'cer\xf3n'))
# cerón
Когда вы берете объект unicode и вызываете его метод decode
с кодеком 'utf-8'
, Python сначала пытается преобразовать объект unicode в строковый объект, а затем вызывает декодирование строкового объекта ('utf-8').
Иногда преобразование из объекта unicode в строковый объект выходит из строя, потому что Python2 по умолчанию использует ascii-кодек.
Итак, в общем, никогда не пытайтесь декодировать объекты unicode. Или, если вы должны попробовать, затащите его в блок try..except. Могут быть несколько кодеков, для которых декодирование юникодовых объектов работает в Python2 (см. Ниже), но они были удалены в Python3.
Смотрите этот билет с ошибкой Python для интересного обсуждения проблемы,
а также блог Guido van Rossum:
"Мы принимаем немного разные подход к кодекам: в то время как в Python 2, кодеки могут принимать либо Unicode, либо 8 бит в качестве входных данных и вывода, в Py3k, кодирование всегда перевод с Юникода (текст) строка в массив байтов и декодирование всегда идет наоборот направлении. Это означает, что мы должны были снимите несколько кодеков, которые не вписываются в эта модель, например rot13, base64 и bz2 (эти преобразования все еще поддерживается не только через encode/decode API).
Ответ 2
Я не знаю, какой хороший способ избежать проверки isinstance
в вашей функции, но, возможно, кто-то еще будет. Я могу указать, что две странности, которые вы цитируете, - это то, что вы делаете то, что не имеет смысла: пытаться декодировать в Unicode что-то, что уже декодировано в Unicode.
Сначала должен выглядеть первый, который декодирует кодировку UTF-8 этой строки в версии Unicode:
>>> 'cer\xc3\xb3n'.decode('utf-8')
u'cer\xf3n'
И ваш второй должен выглядеть так (не используя строковый литерал u''
Unicode):
>>> unicode('hello', 'utf-8')
u'hello'