Punycode — это представление Unicode с ограниченным подмножеством символов ASCII , используемым для имен хостов Интернета . С помощью Punycode имена хостов, содержащие символы Unicode, транскодируются в подмножество ASCII, состоящее из букв, цифр и дефисов, которое называется подмножеством буква–цифра–дефис (LDH). Например, München ( немецкое название Мюнхена ) кодируется как Mnchen-3ya .
В то время как система доменных имен (DNS) технически поддерживает произвольные последовательности октетов в метках доменных имен, стандарты DNS рекомендуют использовать подмножество LDH ASCII, традиционно используемое для имен хостов, и требуют, чтобы сравнение строк между доменными именами DNS было нечувствительным к регистру. Синтаксис Punycode — это метод кодирования строк, содержащих символы Unicode, такие как интернационализированные доменные имена (IDNA), в подмножество LDH ASCII, предпочитаемое DNS. Он указан в запросе комментариев IETF 3492. [1]
Как указано в RFC 3492, "Punycode является примером более общего алгоритма Bootstring , который позволяет строкам, составленным из небольшого набора "базовых" кодовых точек, уникально представлять любую строку кодовых точек, взятую из большего набора". Punycode определяет параметры для общего алгоритма Bootstring, чтобы соответствовать характеристикам текста Unicode. В этом разделе демонстрируется процедура кодирования Punycode на примере строки "bücher" ( Bücher по -немецки означает книги ), которая переводится в метку "bcher-kva".
Для упрощения алгоритмов кодирования и декодирования не было предпринято никаких попыток предотвратить кодирование некоторыми закодированными значениями недопустимых значений Unicode: однако их следует проверять и обнаруживать во время декодирования.
Punycode разработан для работы со всеми скриптами и для самооптимизации путем попытки адаптироваться к диапазонам набора символов в строке по мере ее работы. Он оптимизирован для случая, когда строка состоит из нуля или более символов ASCII и, кроме того, символов только из одной другой системы скриптов, но справится с любой произвольной строкой Unicode. Обратите внимание, что для использования DNS предполагается, что строка имени домена была нормализована с помощью nameprep и (для доменов верхнего уровня ) отфильтрована по официально зарегистрированной языковой таблице перед punycode, и что протокол DNS устанавливает ограничения на допустимую длину выходной строки Punycode.
Сначала все символы ASCII в строке копируются из ввода в вывод, пропуская любые другие символы. Например, "bücher" копируется в "bcher". Если какие-либо символы были скопированы, т. е. если во вводе был хотя бы один символ ASCII, к выводу добавляется дефис ASCII (например, "bücher" → "bcher-", но "ü" → "").
Обратите внимание, что дефисы сами по себе являются символами ASCII. Таким образом, они могут присутствовать во входных данных, и если это так, они будут скопированы в выходные данные. Это не вызывает двусмысленности: если выходные данные содержат дефисы, тот, который был добавлен, всегда последний. Он отмечает конец символов ASCII.
Символы, не входящие в ASCII, сортируются по значению Unicode, начиная с наименьшего (если символ встречается более одного раза, он сортируется по позиции). Затем каждый кодируется как одно число. Это одно число определяет как место для вставки символа, так и сам символ для вставки.
Закодированное число равно n × j + i . Разделив на n и получив остаток, декодер может определить j и i .
Существует шесть возможных мест для вставки символа в строку "bcher" (включая место перед первым символом и место после последнего). Между последней кодовой точкой ASCII (127 = 0x7F , конец ASCII) и ü (кодовая точка 252 = 0xFC , см. дополнение Unicode Latin-1 ) находится 124 кодовых точки. ü вставляется в позицию 1, после b . Таким образом, кодер добавит число (6 × 124) + 1 = 745 , а декодер может получить их с помощью ⌊745 ÷ 6⌋ = 124 и 745 mod 6 = 1 .
Эти числа строго возрастают. Для второго и последующих вставленных символов записывается разница между числом и предыдущим.
Число кодируется с помощью букв от a до z и цифр от 0 до 9. Это не 36-ричная система счисления, а более сложная схема, описанная ниже, которая позволяет объединять числа, не разделяя их ничем.
Punycode использует обобщенные целые числа переменной длины для представления этих значений. Например, вот как "kva" используется для представления кодового числа 745:
Используется система счисления с порядком little-endian , которая допускает коды переменной длины без отдельных разделителей: цифра ниже порогового значения отмечает, что это самая значимая цифра, следовательно, конец числа. Пороговое значение зависит от позиции в числе, а также от предыдущих вставок, для повышения эффективности. Соответственно, веса цифр меняются.
В этом случае используется система счисления с 36 символами, в которой нечувствительные к регистру буквы от «a» до «z» соответствуют десятичным числам от 0 до 25, а буквы от «0» до «9» соответствуют десятичным числам от 26 до 35. Таким образом, «kva» соответствует десятичной строке «10 21 0».
Для декодирования этой строки символов потребуется последовательность порогов, в данном случае это (1, 1, 26, 26, ...). [2] Вес (или разрядное значение ) наименее значащей цифры всегда равен 1: 'k' (=10) с весом 1 равно 10. После этого вес следующей цифры зависит от первого порога: как правило, для любого n вес ( n +1)-й цифры равен w × (36 − t ), где w — предыдущий вес, а t — порог n -й цифры. Итак, в этом случае второй символ имеет разрядное значение 36 минус предыдущее пороговое значение 1, что равно 35. Следовательно, сумма первых двух символов 'k' (=10) и 'v' (=21) равна 10 × 1 + 21 × 35. Поскольку второй символ не меньше своего порогового значения 1, это еще не все. Однако, поскольку третий символ в этом примере - 'a' (=0), мы можем проигнорировать вычисление его веса. Следовательно, "kva" представляет собой десятичное число (10 × 1) + (21 × 35) = 745.
Число 745 будет закодировано как 10 + 21 × 35 + 0 (основание 35 используется для второй цифры, старшая цифра 0 необходима в качестве терминатора), 10 → 'k', 21 → 'v', 0 → 'a', то есть "bücher" → "bcher-kva".
Сами пороговые значения определяются для каждого последующего закодированного символа с помощью алгоритма, поддерживающего их в диапазоне от 1 до 26 включительно. [3] Затем регистр можно использовать для предоставления информации об исходном регистре строки. [4]
Поскольку специальные символы сортируются по их кодовым точкам с помощью алгоритма кодирования, для вставки второго специального символа в "bücher" первой возможностью является "büücher" с кодом "bcher-kvaa", второй - "bücüher" с кодом "bcher-kvab" и т. д. После "bücherü" с кодом "bcher-kvae" идут коды, представляющие вставку ý, символа Unicode, следующего за ü, начиная с "ýbücher" с кодом "bcher-kvaf" (отличается от "übücher", закодированного "bcher-jvab") и т. д.
Чтобы предотвратить дефисы в немеждународных доменных именах от запуска декодирования Punycode, строка xn--
добавляется к последовательностям Punycode в интернационализированных доменных именах. Это называется ACE (ASCII Compatible Encoding). [5]
Таким образом, доменное имя «bücher.tld» будет представлено в URL как «xn--bcher-kva.tld».
В следующей таблице показаны примеры кодировок Punycode для различных типов входных данных. [6]
s.encode("punycode")
). См. страницу обсуждения .