小议 Javascript 中的 UTF-8

2016-12-30 02:56:05 +08:00
 darluc

直接去读原文

在 Javascript 中处理编码问题有时会令人烦恼。我最近一次遭遇就是在使用浏览器的 atobbtoa 函数时。这些函数用于二进制字符串和 Base64 ASCII 码之间的转换。但是用它们处理 Unicode 字符串时就挂了:

> btoa('\u0227');
Error: INVALID_CHARACTER_ERR: DOM
Exception 5

MDN 上关于 btoa 的文档指出下面来自于 Johan Sundström 的函数可以解决 Unicode 问题:

function encode_utf8(s) {
  return unescape(endcodeURIComponent(s));
}

function decode_utf8(s) {
  return decodeURIComponent(escape(s));
}

用上这些函数后,btoa 就能够给出期望的结果了:

> btoa(encode_utf8('\u0227'));
"yKc=="

Johan 的函数能正常工作,只是让人觉得有点像是黑魔法。所以我决定研究一下 encode_utf8 函数的各个部分,搞明白它是如何工作的。

encodeURIComponent

encode_utf8 函数中先使用了 encodeURIComponentencodeURIComponent最近的 ECMAScript 规范中定义如下:

encodeURIComponent 函数计算产生一个新的 URI ,其中的某些字符被替换为一个,二个,三个,或四个字节的 该字符 UTF-8 编码的转义序列。

令我惊讶的是 encodeURIComponent 函数是与 UTF-8 编码绑定的。而不像其它语言的类似函数,编码方式是作为函数参数传入的。如果你本来就像使用 UTF-8 编码,当然没有问题,如果你想使用别的编码方式就不好使了(当然也有别的方法使用 ArrayBuffers 实现定长编码)。

这儿有个例子是对 Unicode 字符 U+0227 (ȧ,小写的拉丁字母 A 顶上加个点)使用 encodeURIComponent 函数:

> encodeURIComponent('\u0227');
"%C8%A7"

结果是一个百分号编码的字符串,每一段表示该字符串 UTF-8 编码的一个字节。查找 Unicode 字符 U+0227 的文档,我们可以验证该字符 UTF-8 编码的十六进制形式就是 0xC8 0xA7 。

ECMAScript 的定义很模糊,它说 encodeURIComponent 会替换“某些字符”,却没说明这些字符。不过一个 快速的试验显示,任何大于 0x7E 的字符都会被编码,所以我觉得说任何非 ASCII 字符都会被 encocodeURIComponent 编码肯定不会错。由于百分号编码方式只使用了数字 0-9 ,字母 A-F 以及 ‘%’ 字符,可以保证输出结果一定是 ASCII 字符串。

现在我们可以先暂停一下。btoa 函数所需要的就是 ASCII 字符串,encodeURIComponent 函数就已然够用了:

> btoa(encodeURIComponent('\u0227'));
"JUM4JUE3"

不过这样做有一些缺陷。首先,输出的结果字符串与输入的字符串的二进制变了。这只是个原始字节的一个编码版本。这可能在与其它系统交互时造成困难和烦恼。

第二个缺陷是,encodeURIComponent 函数生成了一个更长的字符串。单个 Unicode 字符采用 UTF-8 编码最多占用 4 个字节,经过 URI 编码后就能产生一个 12 个字符长的串。之后在经过 Base64 编码(能使字符串变得更长),最终输出的结果比输入的字符能长许多倍。为了解决字符串长度问题, Johan 找来了 unescape 函数。

阅读完整文章

2536 次点击
所在节点    JavaScript
4 条回复
itkdqwzero
2016-12-30 08:06:45 +08:00
赞一个先
Vhc001
2016-12-30 10:34:34 +08:00
非常感谢,真是受益匪浅!
SuperMild
2016-12-30 10:35:14 +08:00
看到最后发现不是原创,是译文
darluc
2016-12-30 17:08:48 +08:00
@SuperMild 😅

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/331142

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX