Как правильно кодировать символы Unicode в URL?

107

Я знаю нестандартную схему% uxxxx, но это не кажется разумным выбором, так как схема была отклонена W3C.

Несколько интересных примеров:

Сердечный персонаж. Если я введу это в свой браузер:

http://www.google.com/search?q=♥

Затем скопируйте и вставьте его, я вижу этот URL

http://www.google.com/search?q=%E2%99%A5

что заставляет думать, что Firefox (или Safari) делает это.

urllib.quote_plus(x.encode("latin-1"))
'%E2%99%A5'

что имеет смысл, за исключением вещей, которые нельзя закодировать в Latin-1, например символа тройной точки.

Если я наберу URL

http://www.google.com/search?q=…

в свой браузер, затем скопируйте и вставьте, я получаю

http://www.google.com/search?q=%E2%80%A6

назад. Кажется, это результат выполнения

urllib.quote_plus(x.encode("utf-8"))

что имеет смысл, поскольку ... не может быть закодирован с помощью Latin-1.

Но тогда мне непонятно, как браузер знает, следует ли декодировать с помощью UTF-8 или Latin-1.

Поскольку это кажется неоднозначным:

In [67]: u"…".encode('utf-8').decode('latin-1')
Out[67]: u'\xc3\xa2\xc2\x80\xc2\xa6'

работает, поэтому я не знаю, как браузер определяет, декодировать ли это с помощью UTF-8 или Latin-1.

Как правильно поступать со специальными символами, с которыми мне нужно иметь дело?

Джош Гибсон
источник
19
Оба ваших примера закодированы как UTF-8. Первый, конечно, не Latin-1, учитывая, что он состоит из трех байтов ...
Якоб Борг,
2
% E2% 99% A5 - это шестнадцатеричный код для байтовых значений «масти черного сердца» в UTF-8 . Это черное сердце не входит в набор символов Latin-1 .
Ястребиный глаз Паркер,
Чтобы точно увидеть, как и что кодирует браузер (и много другой полезной информации), используйте инструменты разработчика, встроенные в большинство современных браузеров, или получите бесплатный отладчик HTTP, такой как Fiddler .
Hawkeye Parker

Ответы:

65

Я всегда кодировал в UTF-8. Со страницы Википедии о процентном кодировании :

Общий синтаксис URI требует, чтобы новые схемы URI, обеспечивающие представление символьных данных в URI, по сути, представляли символы из незарезервированного набора без перевода и должны преобразовывать все другие символы в байты в соответствии с UTF-8, а затем процентное кодирование этих значений. Это требование было введено в январе 2005 г. с публикацией RFC 3986 . Схемы URI, представленные до этой даты, не затрагиваются.

Похоже, что из-за того, что в прошлом были другие общепринятые способы кодирования URL-адресов, браузеры пытаются использовать несколько методов декодирования URI, но если вы тот, кто выполняет кодирование, вам следует использовать UTF-8.

Джон Биснекер
источник
8
Также следует использовать UTF-8, поскольку это единственная кодировка, разрешенная новым стандартом IRI (RFC 3987, tools.ietf.org/html/rfc3986 ), заменяющим старый стандарт URL.
Реми Лебо
3
На случай, если другие будут так же удивлены, как и я, в тексте комментария @ RemyLebeau упоминается RFC3987, но ссылка ведет на более старую спецификацию 3896. Правильный URL-адрес, очевидно, tools.ietf.org/html/rfc3987
tripleee
Да, извините за это. URI определен RFC 3986, IRI определен RFC 3987.
Реми Лебо,
10

Общее правило, по-видимому, заключается в том, что браузеры кодируют ответы формы в соответствии с типом содержимого страницы, с которой была отправлена ​​форма. Это предположение, что если сервер отправит нам «text / xml; charset = iso-8859-1», то они ожидают ответов в том же формате.

Если вы просто вводите URL-адрес в строке URL-адреса, тогда у браузера нет базовой страницы для работы, и поэтому ему нужно только угадывать. Таким образом, в этом случае кажется, что он все время выполняет utf-8 (поскольку оба ваших ввода производили трехоктетные значения формы).

Печальная правда заключается в том, что AFAIK не существует стандарта для набора символов, значения в строке запроса, или действительно любые символы в URL, должны интерпретироваться как. По крайней мере , в случае значений в строке запроса, нет никаких оснований предполагать , что они обязательно делать , соответствуют персонажам.

Известная проблема заключается в том, что вы должны сообщить своей серверной платформе, какой набор символов вы ожидаете, что строка запроса будет закодирована как --- например, в Tomcat вы должны вызвать request.setEncoding () (или другой аналогичный метод), прежде чем вы вызвать любой из методов request.getParameter (). Нехватка документации по этому вопросу, вероятно, отражает недостаточную осведомленность о проблеме среди многих разработчиков. (Я регулярно спрашиваю собеседников Java, в чем разница между Reader и InputStream, и регулярно получаю пустые взгляды)

аракнид
источник
6
RFC 3987 ( tools.ietf.org/html/rfc3986 ) определяет стандартную кодировку - UTF-8 должен использоваться при кодировании символов, которые не могут быть закодированы иным образом.
Реми Лебо,
8

IRI ( RFC 3987 ) - это последний стандарт, заменяющий стандарты URI / URL ( RFC 3986 и старше). URI / URL-адреса изначально не поддерживают Unicode (ну, RFC 3986 добавляет положения для будущих протоколов на основе URI / URL-адресов для его поддержки, но не обновляет предыдущие RFC). Схема "% uXXXX" является нестандартным расширением, позволяющим использовать Unicode в некоторых ситуациях, но не всеми реализована повсеместно. IRI, с другой стороны, полностью поддерживает Unicode и требует, чтобы текст был закодирован как UTF-8 перед его процентным кодированием.

Реми Лебо
источник
Я хочу увидеть обновление протоколов, чтобы Unicode полностью поддерживался в URL-адресах, а не только через процентное кодирование.
Mathieu J.
1
IRI допускает некодированные символы Unicode, за исключением тех немногих случаев, когда необходимо кодировать зарезервированные символы.
Реми Лебо
6

IRI не заменяют URI, потому что только URI (фактически, ASCII) допустимы в некоторых контекстах, включая HTTP.

Вместо этого вы указываете IRI, и он преобразуется в URI при подключении к сети.

Марк Ноттингем
источник
0

Первый вопрос: что вам нужно? Кодировка UTF-8 - это довольно хороший компромисс между взятием текста, созданного с помощью дешевого редактора, и поддержкой большого количества языков. Что касается браузера, определяющего кодировку, ответ (от веб-сервера) должен сообщить браузеру кодировку. Тем не менее, большинство браузеров будут пытаться угадать, потому что во многих случаях это либо отсутствует, либо неверно. Они предполагают, читая некоторый объем потока результатов, чтобы увидеть, есть ли символ, который не подходит для кодировки по умолчанию. В настоящее время все браузеры (? Я не проверял это, но это довольно близко к истине) по умолчанию использует utf-8.

Поэтому используйте utf-8, если у вас нет веских причин использовать одну из многих других схем кодирования.

Пэт О
источник