Python URL URL без кавычек с последующим Unicode декодирования - PullRequest
12 голосов
/ 28 февраля 2011

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

  • Ожидается: çöasd+fjkls%asd
  • результат: çöasd fjkls%asd

символы utf-8 с двойной кодировкой (%C3%A7 и %C3%B6) декодированы неправильно.
Моя версия Python 2.7 в дистрибутиве Linux.Каков наилучший способ получить ожидаемый результат?

Ответы [ 6 ]

28 голосов
/ 28 февраля 2011

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

Резюме: либо (a) вы начинаете с объекта Unicode и применяете кавычкиили (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 (он же латинский1), вы увидите свой симптоматический мусор (первый символ будет 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.К счастью, все символы Unicode в s3 представимы в этой кодировке (и cp1252 и latin1).

12 голосов
/ 28 февраля 2011

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

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

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

>>> print(urllib.unquote_plus(u'%C3%A7%C3%B6asd+fjkls%25asd'.encode('ascii')).decode('utf8'))
çöasd fjkls%asd
0 голосов
/ 28 февраля 2011

У вас двойная проблема: ваша строка закодирована в кодировке Юникод и содержит символ в кодировке. Некоторые совпадают. Вы можете нормализовать вашу строку в 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
0 голосов
/ 28 февраля 2011

Вы используете 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
0 голосов
/ 28 февраля 2011

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

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

0 голосов
/ 28 февраля 2011

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

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