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

IT技术 javascript decode urldecode
2021-01-30 05:33:42

我不确定这叫什么,所以我在寻找它时遇到了麻烦。如何使用 JavaScripthttp\u00253A\u00252F\u00252Fexample.comto解码带有 unicode 的字符串http://example.com我试过unescape, decodeURIdecodeURIComponent所以我想唯一剩下的就是字符串替换。

编辑:该字符串不是输入的,而是来自另一段代码的子字符串。所以要解决这个问题,你必须从这样的事情开始:

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

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

6个回答

编辑(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

请注意,这似乎比fromCharCode方法快得多jsperf.com/unicode-func-vs-json-parse
2021-03-12 05:33:42
有趣的。我确实不得不在它周围添加引号unescape(JSON.parse('"' + s + '"'));额外引号的原因是什么?这是否使它成为有效的 JSON?
2021-03-17 05:33:42
关于@styfle 答案的重要说明:JSON.parse('"' + s + '"')在处理不受信任的数据使用不要使用JSON.parse('"' + s.replace('"', '\\"') + '"'),否则当输入包含引号时,您的代码将中断
2021-03-22 05:33:42
很好的答案@alexander255,但您实际上想要使用:JSON.parse('"' + str.replace(/\"/g, '\\"' + '"') 来替换整个过程中出现的所有该字符字符串,而不是替换一个。
2021-03-27 05:33:42
对于那些遇到这个并且因为unescape()已被弃用而担心的人在这种情况下的decodeURIComponent()工作原理相同unescape(),所以只需将其替换为它就可以了。
2021-04-02 05:33:42

更新:请注意,这是一个应该适用于旧浏览器或非浏览器平台的解决方案,并且为了教学目的而保持活动状态。请参阅下面的@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函数将字符串解码为正确的形式。

@IoannisKaradimas 在 Javascript 中肯定有弃用之类的东西。通过声明必须始终支持旧浏览器来声明并支持它是完全没有历史的观点。无论如何,任何想使用它并且又想避免的人unescape()都可以使用它decodeURIComponent()在这种情况下,它的工作原理相同。然而,我会推荐 radicand 的方法,因为它更简单,同样受支持且执行速度更快,结果相同(但请务必阅读评论)。
2021-03-13 05:33:42
谢谢。你能解释一下你在做什么吗?看起来正则表达式正在寻找\u前缀而不是 4 个字符的十六进制数字(字母或数字)。替换方法中的函数是如何工作的?
2021-03-16 05:33:42
请注意,这似乎比JSON.parse方法慢得多jsperf.com/unicode-func-vs-json-parse
2021-03-26 05:33:42
你是对的,这需要一个解释,所以我更新了我的帖子。享受!
2021-03-27 05:33:42
很好的解决方案。就我而言,我将从服务器发送的所有国际(非 ascii)字符编码为转义的 unicode,然后在浏览器中使用您的函数将字符解码为正确的 UTF-8 字符。我发现我必须更新以下正则表达式才能捕获所有语言(即泰语)的字符:var r = /\\u([\d\w]{1,})/gi;
2021-03-31 05:33:42

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

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

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

http://example.com

这对某些字符串不起作用,因为引号可能会破坏 JSON 字符串并导致 JSON 解析错误。在这些情况下,我使用了另一个答案(stackoverflow.com/a/7885499/249327)。
2021-03-21 05:33:42

JSON.decode为此使用会带来您必须注意的重大缺点:

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

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


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

解释:

源代码大约有 250 行,所以我不会在这里全部包含它,但本质上它使用以下正则表达式来查找所有转义序列,然后使用parseInt(string, 16)解码 base-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

我没有足够的代表将其放在对现有答案的评论中:

unescape仅在使用 URI(或任何编码的 utf-8)时才被弃用,这可能是大多数人需要的情况。encodeURIComponent将 js 字符串转换为转义的 UTF-8,并且decodeURIComponent仅适用于转义的 UTF-8 字节。它抛出一个错误,decodeURIComponent('%a9'); // error因为扩展的 ascii 不是有效的 utf-8(即使它仍然是一个 unicode 值),而unescape('%a9'); // ©因此在使用 decodeURIComponent 时你需要知道你的数据。

decodeURIComponent 将无法处理"%C2"或任何单独的字节,0x7f因为在 utf-8 中表示代理的一部分。然而,decodeURIComponent("%C2%A9") //gives you ©Unescape 无法正常工作// ©并且不会抛出错误,因此如果您不知道自己的数据,则 unescape 可能会导致错误代码。