Алгоритм преобразования интернационального домена

Материал из свободной русской энциклопедии «Традиция»
Перейти к: навигация, поиск

Имеющаяся доменная система позволяет использовать весьма ограниченный набор символов в доменных именах — 26 латинских букв и несколько дополнительных знаков. По этой причине международные названия доменов пришлось ввести алгоритм, который однозначно преобразует цепь знаков Уникода в цепь знаков, доступных для использования в доменных именах, и наоборот. Таким алгоритмом стал алгоритм, называемый «Punycode». Для того, чтобы программа могла отличить доменное имя, которое нужно представить как международное (цепь знаков в Уникоде), от остальных, к результату преобразования имени домена по упомянутому алгоритму добавляется префикс «xn--».

Пример реализации[править]

Ниже приведён пример реализации этого алгоритма на языке Глагол.

ОТДЕЛ Punycode;
ИСПОЛЬЗУЕТ
  Асм ИЗ "...\Отделы\Иное\",
  Цепь ИЗ "...\Отделы\Иное\",
  Буква ИЗ "...\Отделы\Иное\",
  Вывод ИЗ "...\Отделы\Обмен\";

ЗАДАЧА РаскодРазр(c: ЦЕЛ): ЦЕЛ;
УКАЗ
  ЕСЛИ c - 48 < 10 ТО ВОЗВРАТ c - 22
  АЕСЛИ c - 65 < 26 ТО ВОЗВРАТ c - 65
  АЕСЛИ c - 97 < 26 ТО ВОЗВРАТ c - 97
  ИНАЧЕ ВОЗВРАТ 36 КОН
КОН РаскодРазр;

ЗАДАЧА ЗакодРазр(d: ЦЕЛ; f: КЛЮЧ): ЦЕЛ;
УКАЗ
  ЕСЛИ d < 26 ТО ЕСЛИ f ТО ВОЗВРАТ d + 65 ИНАЧЕ ВОЗВРАТ d + 97 КОН
  ИНАЧЕ ЕСЛИ f ТО ВОЗВРАТ d - 10 ИНАЧЕ ВОЗВРАТ d + 22 КОН КОН
КОН ЗакодРазр;

ЗАДАЧА Адапт(д, н: ЦЕЛ; п: КЛЮЧ): ЦЕЛ;
ПЕР
  сч: ЦЕЛ;
УКАЗ
  ЕСЛИ п ТО д := УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ(д / 700))) ИНАЧЕ д := Асм.Сдвиг(д, -1) КОН;
  УВЕЛИЧИТЬ(д, УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ(д / н))));
  сч := 0;
  ПОКА д > Асм.Сдвиг(((36 - 1) * 26), -1) ВЫП
    д := УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ(д / ( 36 - 1 )))); УВЕЛИЧИТЬ(сч, 36)
  КОН;
  ВОЗВРАТ УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ(сч + (36 - 1 + 1) * д / (д + 38))))
КОН Адапт;

ЗАДАЧА ЗакодОсн(b: ЦЕЛ; f: КЛЮЧ): ЦЕЛ;
УКАЗ
  ЕСЛИ b - 97 < 26 ТО УМЕНЬШИТЬ(b, 32) КОН;
  ЕСЛИ НЕ f И (b - 65 < 26) ТО ВОЗВРАТ b + 32 ИНАЧЕ ВОЗВРАТ b КОН
КОН ЗакодОсн;

ЗАДАЧА Закодировать-(вход-, выход+: РЯД ИЗ ЗНАК; учРег: КЛЮЧ);
ПЕР
  входС: ДОСТУП К РЯД ИЗ ЗНАК;
  n, d, h, b, a, j, m, q, k, t, i, сч: ЦЕЛ;
  регБукв: ДОСТУП К РЯД ИЗ КЛЮЧ;
  длинаВхода, размерВыхода: ЦЕЛ;
  ЗАДАЧА ДобавитьЗнак(знак: ЗНАК): КЛЮЧ;
  УКАЗ
    ЕСЛИ сч >= размерВыхода ТО Вывод.Цепь("Память для результата слишком мала."); ВОЗВРАТ ОТКЛ КОН;
    выход[сч] := знак; УВЕЛИЧИТЬ(сч);
    ВОЗВРАТ ВКЛ
  КОН ДобавитьЗнак;
УКАЗ
  ОБНУЛИТЬ(выход);
  длинаВхода := ДЛИНА(вход);
  СОЗДАТЬ(регБукв, длинаВхода);
  СОЗДАТЬ(входС, вход);
  Цепь.ВСтрочные(входС^);
  ЕСЛИ учРег ТО
    ОТ j := 0 ДО длинаВхода-1 ВЫП регБукв[j] := входС[j] # вход[j] КОН
  КОН;
  размерВыхода := РАЗМЕР(выход);
  сч := 0; n := 80H; d := 0; a := 72;
  ОТ j := 0 ДО длинаВхода-1 ВЫП
    ЕСЛИ ВЦЕЛ(входС[j]) < 80H ТО
      ЕСЛИ учРег ТО
        ЕСЛИ НЕ ДобавитьЗнак(ВЗНАК(ЗакодОсн(ВЦЕЛ(входС[j]), регБукв[j]))) ТО ВОЗВРАТ КОН
      ИНАЧЕ
        ЕСЛИ НЕ ДобавитьЗнак(входС[j]) ТО ВОЗВРАТ КОН
      КОН
    КОН
  КОН;
  b := ДЛИНА(выход); h := b;
  ЕСЛИ b > 0 ТО ЕСЛИ НЕ ДобавитьЗнак(2DX) ТО ВОЗВРАТ КОН КОН;
  ПОКА h < длинаВхода ВЫП
    m := МАКС(ЦЕЛ);
    ОТ j := 0 ДО длинаВхода-1 ВЫП
      i := ВЦЕЛ(входС[j]);
      ЕСЛИ (i >= n) И (i < m) ТО m := i КОН
    КОН;
    ЕСЛИ (m - n > ЦЕЛЧАСТЬ((МАКС(ЦЕЛ) - d) / (h + 1))) ТО Вывод.Цепь("Переполнение."); ВОЗВРАТ КОН;
    УВЕЛИЧИТЬ(d, (m - n) * (h + 1));
    n := m;
    ОТ j := 0 ДО длинаВхода-1 ВЫП
      i := ВЦЕЛ(входС[j]);
      ЕСЛИ i < n ТО
        УВЕЛИЧИТЬ(d);
        ЕСЛИ d > МАКС(ЦЕЛ) ТО Вывод.Цепь("Переполнение."); ВОЗВРАТ КОН
      КОН;
      ЕСЛИ i = n ТО
        q := d; k := 36;
        КОЛЬЦО
          ЕСЛИ k <= a ТО t := 1 АЕСЛИ k >= a + 26 ТО t := 26 ИНАЧЕ t := k - a КОН;
          ЕСЛИ q < t ТО ВЫХОД КОН;
          ЕСЛИ НЕ ДобавитьЗнак(ВЗНАК(ЗакодРазр(t + (q - t) ОСТАТОК (36 - t), ОТКЛ))) ТО ВОЗВРАТ КОН;
          q := УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ((q - t) / (36 - t))));
          УВЕЛИЧИТЬ(k, 36)
        КОН;
        ЕСЛИ НЕ ДобавитьЗнак(ВЗНАК(ЗакодРазр(q, учРег И регБукв[j]))) ТО ВОЗВРАТ КОН;
        a := Адапт(d, h + 1, h = b); d := 0; УВЕЛИЧИТЬ(h)
      КОН
    КОН;
    УВЕЛИЧИТЬ(d); УВЕЛИЧИТЬ(n)
  КОН
КОН Закодировать;

ЗАДАЧА Раскодировать-(вход-, выход+: РЯД ИЗ ЗНАК; учРег: КЛЮЧ);
ПЕР
  сч, дл, длинаВхода, размерВыхода, r, i, j, n, w, k, t, b, o: ЦЕЛ;
  регБукв: ДОСТУП К РЯД ИЗ КЛЮЧ;
УКАЗ
  ОБНУЛИТЬ(выход);
  n := 80H; i := 0; b := 72;
  длинаВхода := ДЛИНА(вход);
  ЕСЛИ учРег ТО СОЗДАТЬ(регБукв, длинаВхода) КОН;
  дл := 0; сч := длинаВхода;
  ПОВТОРЯТЬ УМЕНЬШИТЬ(сч); ЕСЛИ вход[сч] = 2DX ТО дл := сч; сч := 0 КОН ДО сч = 0;
  размерВыхода := РАЗМЕР(выход);
  ЕСЛИ размерВыхода < дл ТО Вывод.Цепь("Память для результата слишком мала."); ВОЗВРАТ КОН;
  ОТ сч := 0 ДО дл-1 ВЫП
    ЕСЛИ учРег ТО регБукв[сч] := ВЦЕЛ(вход[сч])-65 < 26 КОН;
    ЕСЛИ ВЦЕЛ(вход[сч]) >= 80H ТО Вывод.Цепь("Ошибочные входные данные."); ВОЗВРАТ КОН;
    выход[сч] := вход[сч]
  КОН;
  ЕСЛИ дл > 0 ТО сч := дл+1 ИНАЧЕ сч := 0 КОН; o := дл;
  ПОКА сч < длинаВхода ВЫП
    j := i; w := 1; k := 36;
    КОЛЬЦО
      ЕСЛИ сч >= длинаВхода ТО Вывод.Цепь("Ошибочные входные данные."); ВОЗВРАТ КОН;
      r := РаскодРазр(ВЦЕЛ(вход[сч])); УВЕЛИЧИТЬ(сч);
      ЕСЛИ r >= 36 ТО Вывод.Цепь("Ошибочные входные данные."); ВОЗВРАТ КОН;
      ЕСЛИ r > ЦЕЛЧАСТЬ((МАКС(ЦЕЛ) - i) / w) ТО Вывод.Цепь("Переполнение."); ВОЗВРАТ КОН;
      УВЕЛИЧИТЬ(i, r * w);
      ЕСЛИ k <= b ТО t := 1 ИНАЧЕ ЕСЛИ k >= b + 26 ТО t := 26 ИНАЧЕ t := k - b КОН КОН;
      ЕСЛИ r < t ТО ВЫХОД КОН;
      ЕСЛИ w > ЦЕЛЧАСТЬ(МАКС(ЦЕЛ) / (36 - t)) ТО Вывод.Цепь("Переполнение."); ВОЗВРАТ КОН;
      w := w * (36 - t); УВЕЛИЧИТЬ(k, 36)
    КОН;
    УВЕЛИЧИТЬ(o); b := Адапт(i - j, o, j = 0);
    ЕСЛИ ЦЕЛЧАСТЬ(i / o) > МАКС(ЦЕЛ) - n ТО Вывод.Цепь("Переполнение."); ВОЗВРАТ КОН;
    УВЕЛИЧИТЬ(n, УЗК(ВШИРЦЕЛ(ЦЕЛЧАСТЬ(i / o)))); i := i ОСТАТОК o;
    ЕСЛИ o >= размерВыхода ТО Вывод.Цепь("Память для результата слишком мала."); ВОЗВРАТ КОН;
    ЕСЛИ учРег ТО
      ОТ j := ДЛИНА(выход)-1 ДО i ПО -1 ВЫП регБукв[j+1] := регБукв[j] КОН;
      регБукв[i] := ВЦЕЛ(вход[сч-1])-65 < 26
    КОН;
    Цепь.ВставитьЗнак(ВЗНАК(n), выход, i); УВЕЛИЧИТЬ(i);
    ЕСЛИ учРег ТО
      ОТ j := 0 ДО ДЛИНА(выход)-1 ВЫП
        ЕСЛИ регБукв[j] ТО выход[j] := Буква.ВЗаглавную(выход[j]) КОН
      КОН
    КОН
  КОН
КОН Раскодировать;

КОН Punycode.

Описание[править]

В отделе «Punycode» представлены две задачи, выполняющие преобразование доменного имени:

  • «Закодировать» — преобразует цепь знаков в Уникоде в код, например, «Русский» в «h1acbXfam», при этом второй параметр указывает, учитывать ли регистр букв при преобразовании или нет;
  • «Раскодировать» — выполняет обратное преобразование, т.е. кода в цепь знаков, например, «h1alFfa9f» в «Россия», второй параметр также влияет на учитывание регистра.