用JavaScript和Python计算Unicode字符串的字节数

标签:JavaScript, Python

JavaScript中的字符串有个length属性,不过那是表示字符数。我在做文件分割时需要统计字节数,所以就找了找这个。

UTF-8的编码规则很简单,只有二条:
  1. 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
  2. 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

下面是Unicode转换到UTF-8的方法:
Unicode(16进制)UTF-8 字节流(二进制)
000000 - 00007F0xxxxxxx (1字节)
000080 - 0007FF110xxxxx 10xxxxxx (2字节)
000800 - 00FFFF1110xxxx 10xxxxxx 10xxxxxx (3字节)
010000 - 10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx(4字节)
这里只关注字节数即可。

方法如下:
function byteLength(s) {
  var totalLength = 0;
  var charCode;
  for (var i = 0; i < s.length; ++i) {
    charCode = s.charCodeAt(i);
    if (charCode < 0x0080) {
      ++totalLength;
    } else if (charCode < 0x0800) {
      totalLength += 2;
    } else if (charCode <= 0xffff) {
      totalLength += 3;
    } else {
      totalLength += 4;
    }
  }
  return totalLength;
}
当然,这是UTF-8编码。如果是GBK编码,只需要判断是否小于0x0080,小于为1字节,否则为2字节(由于字符数有限,不存在超过2字节的字符)。

顺便说下Python中的方法。
Python的len也是计算字符数,但是如果用encode方法把Unicode转为GBK或UTF-8编码的字符串后,此时的len就是字节数了。
给个演示:
>>> s=u'好'
>>> len(s)
1
>>> len(s.encode('gbk'))
2
>>> len(s.encode('utf8'))
3
>>> s=u'Google vs 百度'
>>> len(s)
12
>>> len(s.encode('gbk'))
14
>>> len(s.encode('utf8'))
16
很显然,GBK编码比UTF-8编码要少占用空间,特别是用来记录中文时。

不过准确来说,上文中我用的字符这个术语并不准确,所以顺便介绍一下code point
Unicode中一共有1114112个code point。其中,代码点小于FFFF的属于基本多文种平面(Basic Multilingual Plane,简称BMP),包含在UCS-2中,可以用U+xxxx表示,Python里写为\uxxxx。而Unicode中还有其他16种辅助平面,这些平面的字符的代码点都超过FFFF,超出了UCS-2的范围,因此改用UCS-4,只能用U-xxxxxx来表示,Python里写为\U00xxxxxx。

一个code point可以对应一个Unicode字符或符号(glyph),但实际意义上的字符和Unicode字符却并非一一对应的。
例如这2个字符:&#196;和&#196;。虽然看上去很像,但它们是不同的code point组成的:前者是U+00C4,后者是U+0041 U+0308这2个code point的组合。
并非只有国外的字符才有这种情况,汉语拼音实际上也是如此,例如e和ˊ就可以组成é。
甚至还可以用任意多的code point组成一个蝌蚪文

最后还得提到一点,那就是code unit。它指的是编码时采用的最小字符单位。
对C而言,一个code unit就是char,为1字节(在32位系统上为8位)。1或多个char组成一个Unicode字符,但这需要程序员自己去处理。你也可以用而w_char作为code unit,它可以表示一个UCS-2字符(BMP范围内),但并不能处理辅助平面的字符,因此需要自己实现UTF-16。
对Java而言,一个code unit就是char,为2字节(16位)。1个char可以表示一个UCS-2字符。Java 1.5以后增加了对UTF-16的支持,于是就变成可变长编码了,1或2个char可以表示一个UCS-4字符。但是处理辅助平面的字符时需要使用新增的API。
对Python而言,这是由编译时的参数决定的。较新的版本中,在Windows平台上采用的是UCS-2(准确来说是输入采用UTF-16,因为不存在UCS-2编码,而内部表示为UCS-2,因此一个UCS-4 code point可以表示为1或2个UCS-2 code unit),而Unix上则是UCS-4。在计算长度时,目前的实现是返回code unit的数量,而不是code point的数量。
然而无论哪种语言,都最多只能帮你做到code point这级,而不会帮你处理组合字符。

0条评论 你不来一发么↓

    想说点什么呢?