如何使用转义的unicode解码字符串?


88

我不确定这叫什么,所以我很难找到它。如何从解码使用Unicode字符串http\u00253A\u00252F\u00252Fexample.comhttp://example.com使用JavaScript?我想unescapedecodeURIdecodeURIComponent所以我想唯一剩下的就是字符串替换。

编辑:未键入字符串,而是来自另一段代码的子字符串。因此,要解决该问题,您必须先从以下内容开始:

var s = 'http\\u00253A\\u00252F\\u00252Fexample.com';

我希望这表明为什么unescape()不起作用。


字符串从哪里来?
卡梅伦

@Cameron:该字符串来自我调用innerHTML的脚本。这就是alex的答案不起作用的原因。
styfle 2011年

Answers:


108

编辑(2017-10-12)

@MechaLynx和@ Kevin-Weber指出,unescape()非浏览器环境已弃用,并且TypeScript中不存在。decodeURIComponent是替代产品。为了获得更大的兼容性,请改用以下内容:

decodeURIComponent(JSON.parse('"http\\u00253A\\u00252F\\u00252Fexample.com"'));
> 'http://example.com'

原始答案:

unescape(JSON.parse('"http\\u00253A\\u00252F\\u00252Fexample.com"'));
> 'http://example.com'

您可以将所有工作卸载到 JSON.parse


6
有趣。我确实必须在其周围添加引号unescape(JSON.parse('"' + s + '"'));。额外引号的原因是什么?这使它成为有效的JSON吗?
styfle

1
请注意,这似乎比该fromCharCode方法快得多:jsperf.com/unicode-func-vs-json-parse
nrabinowitz 2014年

17
有关@styfle答案的重要说明:JSON.parse('"' + s + '"')在处理不受信任的数据时,请不要使用JSON.parse('"' + s.replace('"', '\\"') + '"'),否则,当输入包含引号时,代码将中断
ntninja 2014年

7
@ alexander255是一个很好的答案,但是您实际上想使用:JSON.parse('“'+ str.replace(/ \” / g,'\\“'+'”')替换整个字符中所有出现的该字符字符串,而不是替换一个字符串
CS

2
对于那些遇到这种情况并因unescape()已被弃用而担心的人,其decodeURIComponent()工作方式与unescape()本例相同,因此只需将其替换为您就可以了。
mechalynx

116

更新:请注意,这是一种解决方案,应适用于较旧的浏览器或非浏览器平台,并且出于说明目的而保持有效。请参阅下面的@radicand答案,以获取最新的答案。


这是一个Unicode转义字符串。首先将字符串转义,然后使用unicode进行编码。转换回普通:

var x = "http\\u00253A\\u00252F\\u00252Fexample.com";
var r = /\\u([\d\w]{4})/gi;
x = x.replace(r, function (match, grp) {
    return String.fromCharCode(parseInt(grp, 16)); } );
console.log(x);  // http%3A%2F%2Fexample.com
x = unescape(x);
console.log(x);  // http://example.com

解释一下:我使用正则表达式查找\u0025。但是,由于替换操作只需要该字符串的一部分,因此我使用括号将要重用的部分隔离开来,0025。这个孤立的部分称为组。

gi表达式末尾的部分表示它应该匹配字符串中的所有实例,而不仅仅是第一个实例,并且匹配应该不区分大小写。在该示例中,这看起来似乎不必要,但它增加了多功能性。

现在,要从一个字符串转换为下一个字符串,我需要在每个匹配项的每一组上执行一些步骤,而我不能仅通过转换字符串来做到这一点。有用的是,String.replace操作可以接受一个函数,该函数将为每个匹配项执行。该函数的返回将替换字符串中的匹配项本身。

我使用此函数接受的第二个参数,这是我需要使用的组,并将其转换为等效的utf-8序列,然后使用内置unescape函数将字符串解码为正确的形式。


3
谢谢。你能解释一下你在做什么吗?看起来正则表达式正在寻找\u前缀,而不是4个字符的十六进制数字(字母或数字)。replace方法中的功能如何工作?
styfle

1
您是对的,需要解释,所以我更新了我的帖子。请享用!
Ioannis Karadimas 2011年

1
很好的解决方案。就我而言,我将服务器发送的所有国际(非ascii)字符编码为转义的unicode,然后在浏览器中使用您的函数将字符解码为正确的UTF-8字符。我发现我必须更新以下正则表达式才能捕获所有语言(例如泰语)中的字符:var r = /\\u([\d\w]{1,})/gi;
Nathan Hanna 2014年

2
请注意,这似乎比该JSON.parse方法要慢得多:jsperf.com/unicode-func-vs-json-parse
nrabinowitz 2014年

1
@IoannisKaradimas最肯定的是Javascript中有一个过时的东西。通过声明必须始终支持较旧的浏览器来声明并支持它,这完全是历史性的观点。无论如何,任何想使用它并且也想避免的人unescape()都可以decodeURIComponent()代替使用。在这种情况下,它的工作原理相同。但是,我建议使用radicand的方法,因为它更简单,受支持且执行起来更快,并且结果相同(但是请务必阅读注释)。
mechalynx

21

请注意,使用的unescape()过时和不与打字稿编译工作,例如。

根据radicand的回答和下面的评论部分,这是一个更新的解决方案:

var string = "http\\u00253A\\u00252F\\u00252Fexample.com";
decodeURIComponent(JSON.parse('"' + string.replace(/\"/g, '\\"') + '"'));

http://example.com


这不适用于某些字符串,因为引号可能会破坏JSON字符串并导致JSON解析错误。在这些情况下,我使用了其他答案(stackoverflow.com/a/7885499/249327)。
nickdos

2

我没有足够的代表将其放在现有答案的注释中:

unescape仅不推荐用于URI(或任何编码的utf-8),大多数人可能都是这种情况。encodeURIComponent将js字符串转换为转义的UTF-8,并且decodeURIComponent仅适用于转义的UTF-8字节。它会decodeURIComponent('%a9'); // error因为诸如扩展ascii无效的utf-8(即使它仍然是unicode值)之类的错误而引发错误,而unescape('%a9'); // ©因此,在使用解码URIComponent时您需要了解数据。

由于在utf-8中表示代理的一部分,所以解码URIComponent不能继续工作,也不能"%C2"结束任何单独的字节0x7f。但是,decodeURIComponent("%C2%A9") //gives you ©Unescape无法在此上正常工作// ©,也不会引发错误,因此,如果您不知道数据,unescape可能会导致错误代码。


1

使用JSON.decode这种带有显著的缺点,你必须知道的:

  • 您必须将字符串用双引号引起来
  • 许多字符不受支持,必须自己转义。例如,通过以下任到的JSON.decode(在双引号包裹它们之后)会出错,即使这些都是有效的:\\n\n\\0a"a
  • 它不支持十六进制转义: \\x45
  • 它不支持Unicode代码点序列: \\u{045}

还有其他警告。从本质上讲,JSON.decode用于此目的是一种黑客行为,并且无法像您一直期望的那样工作。您应该坚持使用该JSON库来处理JSON,而不是字符串操作。


最近,我本人遇到了这个问题,想要一个功能强大的解码器,所以我最终自己写了一个。它是完整且经过全面测试的,可在这里找到:https : //github.com/iansan5653/unraw。它尽可能地模仿JavaScript标准。

说明:

源代码大约有250行,因此在这里我将不包括所有内容,但是本质上,它使用以下正则表达式查找所有转义序列,然后使用parseInt(string, 16)对它们进行解析以解码以16为底的数字,然后String.fromCodePoint(number)获取相应的字符:

/\\(?:(\\)|x([\s\S]{0,2})|u(\{[^}]*\}?)|u([\s\S]{4})\\u([^{][\s\S]{0,3})|u([\s\S]{0,4})|([0-3]?[0-7]{1,2})|([\s\S])|$)/g

已注释(注意:此正则表达式与所有转义序列匹配,包括无效的转义序列。如果字符串在JS中引发错误,则在我的库中引发错误[即,'\x!!'将出错]):

/
\\ # All escape sequences start with a backslash
(?: # Starts a group of 'or' statements
(\\) # If a second backslash is encountered, stop there (it's an escaped slash)
| # or
x([\s\S]{0,2}) # Match valid hexadecimal sequences
| # or
u(\{[^}]*\}?) # Match valid code point sequences
| # or
u([\s\S]{4})\\u([^{][\s\S]{0,3}) # Match surrogate code points which get parsed together
| # or
u([\s\S]{0,4}) # Match non-surrogate Unicode sequences
| # or
([0-3]?[0-7]{1,2}) # Match deprecated octal sequences
| # or
([\s\S]) # Match anything else ('.' doesn't match newlines)
| # or
$ # Match the end of the string
) # End the group of 'or' statements
/g # Match as many instances as there are

使用该库:

import unraw from "unraw";

let step1 = unraw('http\\u00253A\\u00252F\\u00252Fexample.com');
// yields "http%3A%2F%2Fexample.com"
// Then you can use decodeURIComponent to further decode it:
let step2 = decodeURIComponent(step1);
// yields http://example.com
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.