Python url unquote unicode

У меня есть строка unicode, например '%C3%A7%C3%B6asd+fjkls%25asd', и я хочу декодировать эту строку.
Я использовал urllib.unquote_plus (str), но он работает неправильно.

- expected : 'çöasd+fjkls%asd '
- result : 'çöasd fjkls%asd'

двойные кодированные символы utf-8 (%C3%A7 and %C3%B6) декодируются неправильно.
моя версия python - 2,7 под дистрибутивом linux
Каков наилучший способ получить ожидаемый результат?

заблаговременно

Ответ 1

У вас есть 3 или 4 или 5 проблемы... но repr() и unicodedata.name() - ваши друзья; они однозначно показывают вам, что у вас есть, без путаницы, порожденной людьми с разными консольными кодировками, сообщающими результаты print fubar.

Сводка: либо (а) вы начинаете с объекта unicode и применяете к нему функцию unquote, либо (b) вы начинаете с объекта str, а ваша консольная кодировка - не UTF-8.

Если вы говорите, что начинаете с объекта unicode:

>>> s0 = u'%C3%A7%C3%B6asd+fjkls%25asd'
>>> print repr(s0)
u'%C3%A7%C3%B6asd+fjkls%25asd'

Это случайная чепуха. Если вы примените к нему urllibX.unquote_YYYY(), вы получите еще один бессмысленный unicode-объект (u'\xc3\xa7\xc3\xb6asd+fjkls%asd'), который вызовет проявленные вами симптомы при печати. Вы должны немедленно конвертировать исходный объект unicode в объект str:

>>> s1 = s0.encode('ascii')
>>> print repr(s1)
'%C3%A7%C3%B6asd+fjkls%25asd'

то вы должны его недооценить:

>>> import urllib2
>>> s2 = urllib2.unquote(s1)
>>> print repr(s2)
'\xc3\xa7\xc3\xb6asd+fjkls%asd'

Рассматривая первые 4 байта, он кодируется в UTF-8. Если вы сделаете print s2, он будет выглядеть нормально, если ваша консоль ожидает UTF-8, но если он ожидает ISO-8859-1 (aka latin1), вы увидите свой симптомный мусор (первый char будет A-тильдой). Пусть парк, который задумался и превратит его в объект Unicode:

>>> s3 = s2.decode('utf8')
>>> print repr(s3)
u'\xe7\xf6asd+fjkls%asd'

и осмотрите его, чтобы узнать, что у нас есть:

>>> import unicodedata
>>> for c in s3[:6]:
...     print repr(c), unicodedata.name(c)
...
u'\xe7' LATIN SMALL LETTER C WITH CEDILLA
u'\xf6' LATIN SMALL LETTER O WITH DIAERESIS
u'a' LATIN SMALL LETTER A
u's' LATIN SMALL LETTER S
u'd' LATIN SMALL LETTER D
u'+' PLUS SIGN

Похоже, вы сказали, что ожидаете. Теперь мы переходим к вопросу о его отображении на консоли. Примечание: не волнуйтесь, когда вы видите "cp850"; Я делаю это переносимо и просто выполняю это в командной строке в Windows.

>>> import sys
>>> sys.stdout.encoding
'cp850'
>>> print s3
çöasd+fjkls%asd

Примечание. Объект unicode был явно закодирован с использованием sys.stdout.encoding. К счастью, все символы юникода в s3 могут быть представлены в этой кодировке (и cp1252 и latin1).

Ответ 2

Использование unquote или unquote_plus даст вам байтовую строку. Если вам нужна строка Unicode, вы должны декодировать строку байта в unicode:

>>> print(urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd').decode('utf8'))
çöasd fjkls%asd
>>> 

По сравнению с:

>>> print(urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd'))
çöasd fjkls%asd
>>> 

Обратите внимание, что ваша строка ввода должна быть байтовой строкой: если вы передадите unicode на unquote/unquote_plus, тогда вы получите немного беспорядка. Если это так, то сначала закодируйте его:

>>> print(urllib.unquote_plus(u'%C3%A7%C3%B6asd+fjkls%25asd'.encode('ascii')).decode('utf8'))
çöasd fjkls%asd

Ответ 3

Попробуйте urllib2 еще раз:

print urllib2.unquote('%C3%A7%C3%B6asd+fjkls%25asd')

Ответ 4

'% C3% A7% C3% B6asd + fjkls% 25asd' - это не строка в Юникоде.

Это строка с кодировкой url. Вместо этого используйте urllib2.unquote().

Ответ 5

Вы используете метод unquote_plus, который принимает space во внимание и преобразовывается в +. Просто используйте метод unquote, и все будет в порядке.

>>> import urllib
>>> print urllib.unquote('%C3%A7%C3%B6asd+fjkls%25asd')
çöasd+fjkls%asd
>>> print urllib.unquote_plus('%C3%A7%C3%B6asd+fjkls%25asd')
çöasd fjkls%asd

Ответ 6

У вас двойная проблема: ваша строка кодируется в кодировке Юникод и содержит атрибут urlencoded. Некоторая совпадение. Вы можете нормализовать строку в ascci, чтобы убедиться, что она не будет интерпретироваться неправильно:

>>> s = '%C3%A7%C3%B6asd+fjkls%25asd' # ascii string
>>> print urllib2.unquote(s) # works as expected
çöasd+fjkls%asd
>>> s = u'%C3%A7%C3%B6asd+fjkls%25asd' # unicode string
>>> print urllib2.unquote(s) # decode stuff that it shouldn't
çöasd+fjkls%asd
>>> print urllib2.unquote(s.encode('ascii')) # encode the unicode string to ascii: works!
çöasd+fjkls%asd