小议 Javascript 中的 UTF-8 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
darluc
V2EX    Javascript

小议 Javascript 中的 UTF-8

  •  2
     
  •   darluc 2016-12-30 02:56:05 +08:00 2944 次点击
    这是一个创建于 3207 天前的主题,其中的信息可能已经有所发展或是发生改变。

    直接去读原文

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

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

    MDN 上关于 btoa 的文档指出下面来自于 Johan Sundstrm 的函数可以解决 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 函数。

    阅读完整文章

    4 条回复    2016-12-30 17:08:48 +08:00
    itkdqwzero
        1
    itkdqwzero  
       2016-12-30 08:06:45 +08:00 via Android
    赞一个先
    Vhc001
        2
    Vhc001  
       2016-12-30 10:34:34 +08:00
    非常感谢,真是受益匪浅!
    SuperMild
        3
    SuperMild  
       2016-12-30 10:35:14 +08:00 via iPad
    看到最后发现不是原创,是译文
    darluc
        4
    darluc  
    OP
       2016-12-30 17:08:48 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5244 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 09:20 PVG 17:20 LAX 02:20 JFK 05:20
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86