使用 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() 将导致异常。
将不胜感激。
据我所知,在浏览器中没有对带有遗留字符集的百分比编码方案的内置支持。你必须:
- 找到代表 win-1251 八位字节的 %-escapes,
- 将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"))
- 查找字符串:“%EF%F0%EE%E3%F0%E0%EC%EC”、“%E8%F0%EE%E2%E0%ED%E8%E5”
- 将“%”替换为“,0x”:“,0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC”, “,0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8, 0xE5
- 没有第一个逗号的切片:“0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC”,“0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8,0xE5”
- 拆分为字符串数组:["0xEF","0xF0","0xEE","0xE3","0xF0","0xE0","0xEC","0xEC"], ["0xE8" "0xF0","0xEE","0xE2","0xE0","0xED","0xE8","0xE5"]
- 创建一个字节数组:[239,240,238,227,240,224,236,236],[232,240,238,226,224,237,232,229]
- 将字节解码为字符串 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)
我遇到了一个问题,不幸的是,我还没有找到正确的解决方案:我需要解码 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() 将导致异常。
将不胜感激。
据我所知,在浏览器中没有对带有遗留字符集的百分比编码方案的内置支持。你必须:
- 找到代表 win-1251 八位字节的 %-escapes,
- 将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"))
- 查找字符串:“%EF%F0%EE%E3%F0%E0%EC%EC”、“%E8%F0%EE%E2%E0%ED%E8%E5”
- 将“%”替换为“,0x”:“,0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC”, “,0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8, 0xE5
- 没有第一个逗号的切片:“0xEF,0xF0,0xEE,0xE3,0xF0,0xE0,0xEC,0xEC”,“0xE8,0xF0,0xEE,0xE2,0xE0,0xED,0xE8,0xE5”
- 拆分为字符串数组:["0xEF","0xF0","0xEE","0xE3","0xF0","0xE0","0xEC","0xEC"], ["0xE8" "0xF0","0xEE","0xE2","0xE0","0xED","0xE8","0xE5"]
- 创建一个字节数组:[239,240,238,227,240,224,236,236],[232,240,238,226,224,237,232,229]
- 将字节解码为字符串 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)