Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

手动UTF-8编码

  在一些传输层的代码中有时可能需要传输二进制的UTF-8字符串。虽然encodeURIComponent方法可以对大部分字符进行UTF-8编码,但它得到的是一个16进制的字符串,再转换成数组效率很低。把数据做成16进制字符串的步骤是多余的,直接计算出数组会比它更快。
  想要手动编码UTF-8,首先要了解它的结构。UTF-8由1到4个字节的字符组成,首个字节的最高若干位是二进制的1,随后跟上一个二进制的0,其它字节都由二进制的10开头。越长的UTF-8字符表示的Unicode码位越大。
字节数表示的Unicode范围二进制格式
10x0000-0x007F 0*******
20x0080-0x07FF 110***** 10******
30x0800-0xFFFF 1110**** 10****** 10******
4>0xFFFF 11110*** 10****** 10****** 10******
  上表中,数字部分就是它的固定格式,星号部分就是它所能储存的Unicode位,比如4字节的UTF-8字符里面有21个星号,它就可以储存21个位。实际上Unicode的最大码位就是21位,所以它可以表示所有Unicode字符。使用时候就是拿出字符的Unicode值的二进制填入UTF-8的数据存储部分,并且尽可能的保持UTF-8使用最少的字节数。比如英文字母的Unicode值小于0x7F,使用一个字节的UTF-8字符即可。如果用大于一个字节的UTF-8去储存英文字母将得到一个无效的字符。
  既然知道了格式,我们就可以写个程序来做UTF-8编码。但要注意的是直接调用字符串的charCodeAt得到的是UTF-16的代码值,使用它还需要先转换为Unicode值,这个转换在先前的文章“四字节的UTF-16字符”有介绍过。这里就直接写UTF-8的 <input type="button" value="encodeUTF8" />
<input type="button" value="encodeURIComponent" />
<script>
document.onclick=function(e){
  if(!(e=e.target.value))return;
  var i,s=Array(1000).join("次碳酸钴"),f=window[e];
  console.time(e);
  for(i=0;i<1000;i++)f(s);
  console.timeEnd(e);
};
function encodeUTF8(s){
  var i,r=[],c,x;
  for(i=0;i<s.length;i++)
    if((c=s.charCodeAt(i))<0x80)r.push(c);
    else if(c<0x800)r.push(0xC0+(c>>6&0x1F),0x80+(c&0x3F));
    else {
      if((x=c^0xD800)>>10==0) //对四字节UTF-16转换为Unicode
        c=(x<<10)+(s.charCodeAt(++i)^0xDC00)+0x10000,
        r.push(0xF0+(c>>18&0x7),0x80+(c>>12&0x3F));
      else r.push(0xE0+(c>>12&0xF));
      r.push(0x80+(c>>6&0x3F),0x80+(c&0x3F));
    };
  return r;
}
</script>

  在IE和Chrome中都是手动UTF-8编码比较快的,但FF的情况完全相反。这可能是因为FF的JavaScript引擎本身不给力,而内置函数实现的比较好吧。另外,这个encodeUTF8函数得到的是一个数组,可以很容易地转换成Uint8Array之类的强类型数组作为字节流来使用。而encodeURIComponent得到的是一个字符串,转换成数组还需要很多工序,更何况里面还有一些字符是不被转义的,需要特殊处理。
  总之,在需要使用UTF-8的字节流的场合,手动编码会比较好。
网名:
34.203.213.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^