Найти - Пользователи
Полная версия: utf8-escaped в unicode-строке
Начало » Python для экспертов » utf8-escaped в unicode-строке
1 2 3 4
skavans
Возникла странная проблема: есть файл в формате JSON, в нем, в одном из полей есть URL, который содержит кириллицу в виде search?q=%D1%81%D0%B0%D1%85%D0%B0%D0%BB%D0%B8%D0%BD.
Далее с помощью json.load() создаю dict из него, делаю urllib.unquote() и получаю вот такой объект u'\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'.
Если честно, не могу сказать, что на все 100% понял, как работать с кодировками и encode/decode, но судя по всему, это неправильный объект - utf-escaped строка лежит в unicode-строке..
В связи с этим, прошу помощи в 2 вопросах:
1) как из этого объекта получить нормальную читаемую строку;
2) в чем вообще причина такой ошибки.

Спасибо за ответы.
4kpt
Первое. Где Вы видите ошибку?

Второе. Получить ожидаемые данные:

# coding: utf-8
import quopri
str_ = "%D1%81%D0%B0%D1%85%D0%B0%D0%BB%D0%B8%D0%BD".replace("%", "=")
res = quopri.decodestring(str_).decode("utf-8")
print res

Третье. Это вопрос не для экспертов :)
wbt
\x - это не уникоды, уникоды - \u

>>> a = '\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'
>>> a
'\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'
>>> len(a)
18
>>> type(a)
<type 'str'>
>>> unicode(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 0: ordinal not in range(128)
>>> a = a.decode('utf-8')
>>> a
u'\u043b\u043e\u0442\u044b\u0432\u043b\u043e\u0430\u0442'
>>> len(a)
9
>>> type(a)
<type 'unicode'>
skavans
Насколько я понимаю, ошибка в литере u перед строкой. То есть правильными объектами являются
“\xd0\xbb” или u“\u043b”, а у меня получается u“\xd0\xbb”. То есть байтовое представление символов лежит в Unicode-строке, где должно быть символьное представление. В результате, если сделать print такого объекта, печатаются кракозябры прошу прощения, если недостаточно понятно объяснил в первом посте.
В процессе гугления нашел ответ на похожую проблему: “уберите литеру u перед строкой, потому что это не Unicode, а UTF8”. Но как ее убрать - вот в чем вопрос
wbt
А вот это уже интересно:

>>> a
u'\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'
>>> a.decode('utf-8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-17: ordinal not in range(128)
>>> str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-17: ordinal not in range(128)
>>> bytes(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-17: ordinal not in range(128)
>>> a[0]
u'\xd0'
>>> type(a)
<type 'unicode'>
>>> repr(a)
"u'\\xd0\\xbb\\xd0\\xbe\\xd1\\x82\\xd1\\x8b\\xd0\\xb2\\xd0\\xbb\\xd0\\xbe\\xd0\\xb0\\xd1\\x82'"

python даёт ввести данные, которые заведомо неверны, но не даёт ничего из них получить, потому что они заведомо неверны. как просто поменять тип, не трогая данные - я так и не нашёл.

лучший способ, конечно - это получать правильные данные в самой программе, но вот эта задача сама по себе интересна.
wbt
что-то типа такого:

>>> a.encode('latin1')
'\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'
>>> a.encode('latin1').decode('utf-8')
u'\u043b\u043e\u0442\u044b\u0432\u043b\u043e\u0430\u0442'
>>> print a.encode('latin1').decode('utf-8')
лотывлоат
4kpt
wbt
лотывлоат
Мощно.

Должно получиться по исходной кодировке :)

сахалин
wbt
>>> import urllib
>>> a = urllib.unquote('%D1%81%D0%B0%D1%85%D0%B0%D0%BB%D0%B8%D0%BD')
>>> a
'\xd1\x81\xd0\xb0\xd1\x85\xd0\xb0\xd0\xbb\xd0\xb8\xd0\xbd'
>>> print a.decode('utf-8')
сахалин

каким образом из
'\xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82'
можно получить “сахалин”, если там даже число букв явно разное? если бы это был невалидный utf-8, были бы тоже кракозяблы, наверное, а не текст.
4kpt
:)

Как Вы тогда объясните это:

http://coding-cs.ru/%d0%ba%d0%be%d0%bd%d1%82%d0%b0%d0%ba%d1%82%d1%8b/

Переходим и видим

http://coding-cs.ru/контакты/

Написанный код также выдает:

# coding: utf-8
import quopri
str_ = "%d0%ba%d0%be%d0%bd%d1%82%d0%b0%d0%ba%d1%82%d1%8b".replace("%","=")
res = quopri.decodestring(str_).decode("utf-8")
print res

контакты

P.S. Гляньте сюда http://ru.wikipedia.org/wiki/URL
Раздел “Кодирование URL”.
wbt
print '\xd0\xba\xd0\xbe\xd0\xbd\xd1\x82\xd0\xb0\xd0\xba\xd1\x82\xd1\x8b'.decode('utf-8')
контакты

в utf-8, если байт начинается от C0 до DF, то используется 2 байта.

в строке ‘xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82’ все байты начинаются только с D0 или D1. следовательно, здесь 9 символов. в utf-8. сахалин в 9 символов не попадает никак.

если это не utf-8, то вероятность того, что эти символы попали бы на русские буквы, да ещё и у всех были бы одинаковые стартовые биты - примерно равна 0, а может и ровно 0, и таких вариантов просто не может быть.

Так что, дело очень просто, Ватсон - в строке xd0\xbb\xd0\xbe\xd1\x82\xd1\x8b\xd0\xb2\xd0\xbb\xd0\xbe\xd0\xb0\xd1\x82 закодировано лотывлоат, что бы это не значило.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB