使用 JavaScript 解码 url 编码的 windows-1251 (cp1251) 字符串

Decoding a url-encoded windows-1251 (cp1251) string with JavaScript

我遇到了一个问题,不幸的是,我还没有找到正确的解决方案:我需要解码 url-用 windows-1251 (cp1251) 编码的切片。

我知道有这些方法 - decodeURI()decodeURIComponent(),但它们仅适用于 UTF-8(如我已经明白了)。我找到的解决方案使用了已弃用的方法 escape() 和 unescape()。

例如有序列:

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

方法 decodeURI() 和 decodeURIComponent() 将导致异常。

将不胜感激。

据我所知,在浏览器中没有对带有遗留字符集的百分比编码方案的内置支持。你必须:

  1. 找到代表 win-1251 八位字节的 %-escapes,
  2. 将win-1251八位字节解码为相应的字符(JS String)

下面是一种方法。对于#1,我假设只有 3 个字符的大写转义符需要解码,而字符串的其余部分已经是 ASCII,所以我只使用 inputStr.replace(/%([0-9A-Z]{2})/g,replacerFunction)为此。

对于实际解码,您需要从 win-1251 八位字节到 JS 字符的映射。在下面的示例中,我使用 TextDecoder.decode() API 构建映射,只是为了好玩(以防有人在尝试在 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)