Расшифровка URL-кодированной строки windows-1251 (cp1251) с помощью JavaScript

Я столкнулся с проблемой, к сожалению, я не нашел правильного решения: мне нужно декодировать url-slice, который закодирован в windows-1251 (cp1251).

Я знаю, что есть эти методы - decodeURI() и decodeURIComponent(), но они работают только для UTF-8 (как я понял). Решение, которое я нашел, использует устаревшие методы escape () и unescape ().

Например, есть последовательность:

% EF% F0% EE% E3% F0% E0% EC% EC% E8% F0% E0% E2% E0% ED% E8% E5 (программирование)

Методы decodeURI() и decodeURIComponent() вызовут исключение.

Будем благодарны за помощь.

2 ответа

Решение

Насколько я вижу, в браузере нет встроенной поддержки схемы процентного кодирования с устаревшими кодировками. Вам придется:

  1. найти%-экраны, представляющие октеты win-1251,
  2. декодировать октеты win-1251 в соответствующие символы (JS String)

Ниже приведен один из способов сделать это. Для #1 я предполагаю, что только 3-символьные символы верхнего регистра нуждаются в декодировании, а остальная часть строки уже ASCII, поэтому я просто использую inputStr.replace(/%([0-9A-Z]{2})/g,replacerFunction) за это.

Для фактического декодирования вам необходимо сопоставить октеты win-1251 с символами JS. В приведенном ниже примере я строю отображение, используя API TextDecoder.decode (), просто для удовольствия (и в случае, если кто-то найдет этот ответ при попытке конвертировать между различными кодировками в JS). (Примечание: на данный момент он не поддерживается повсеместно - его поддерживают только Gecko/Blink).

Также есть https://github.com/mathiasbynens/windows-1251, который я изначально хотел использовать для этого ответа, но оказалось, что проще просто составить карту декодирования вручную.

var decodeMap = {};
var win1251 = new TextDecoder("windows-1251");
for (var i = 0x00; i < 0xFF; i++) {
  var hex = (i <= 0x0F ? "0" : "") +      // zero-padded
            i.toString(16).toUpperCase();
  decodeMap[hex] = win1251.decode(Uint8Array.from([i]));
}
// console.log(decodeMap);
// {"10":"\u0010", ... "40":"@","41":"A","42":"B", ... "C0":"А","C1":"Б", ...


// Decodes a windows-1251 encoded string, additionally
// encoded as an ASCII string where each non-ASCII character of the original
// windows-1251 string is encoded as %XY where XY (uppercase!) is a
// hexadecimal representation of that character's code in windows-1251.
function percentEncodedWin1251ToDOMString(str) {
  return str.replace(/%([0-9A-F]{2})/g,
    (match, hex) => decodeMap[hex]);
}

console.log(percentEncodedWin1251ToDOMString("%EF%F0%EE%E3%F0%E0%EC%EC%!%E8%F0%EE%E2%E0%ED%E8%E5a"))

  1. найти строки: "%EF%F0%EE%E3%F0%E0%EC%EC", "%E8%F0%EE%E2%E0%ED%E8%E5"
  2. замените "%" на ",0x": ",0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC", ",0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8,0xE5"
  3. срез без первой запятой: "0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC", "0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8,0xE5"
  4. разбить на массив строк: ["0xEF","0xF0","0xEE","0xE3","0xF0","0xE0","0xEC","0xEC"], ["0xE8","0xF0", "0xEE", "0xE2", "0xE0", "0xED", "0xE8", "0xE5"]
  5. создать массив байтов: [239,240,238,227,240,224,236,236], [232,240,238,226,224,237,232,229]
  6. перекодировать байты в строки win-1251: "программ", "ирование" и заменить ими
      var win1251 = new TextDecoder("windows-1251"),
s = "%EF%F0%EE%E3%F0%E0%EC%EC%!%E8%F0%EE%E2%E0%ED%E8%E5a"
s = s.replace(/(?:%[0-9A-F]{2})+/g,
s => win1251.decode(new Uint8Array(
s.replace(/%/g, ",0x").slice(1).split(",")
)))
alert(s)
Другие вопросы по тегам