转载请注明出处: http://qiudeqing.com/general/2016/01/24/unicode-and-utf.html

Unicode

Unicode是一个字符集, 为每一个字符提供一个唯一的编号, 由Unicode Consortium维护.

详细信息参考wikipedia: Unicode, http://www.unicode.org/

code point

Unicode为字符提供的唯一编号叫做code point或者code position.

Unicode Plane

在Unicode标准中, 一个plane由连续的65536(2的十六次方) 个code point组成.

一共有17个plane, 位置编码的前两位表示plane编码, 即hh hhhh的前两位.

plane 0 叫做Basic Multilingual Plane, 其余的plane叫做Supplementary plane

Basic Multilingual Plane

BMP几乎包括现代语言的所有字符和大量图标. 其中的大部分code point用于编码CJK字符

UTF

Unicode Transformation Format(UTF)是用于将每一个Unicode code point映射为一个唯一的字节序列的编码算法. 常见的编码算法有UTF8, UTF-16, UTF-32.

code unit

编码算法用于表示字符的最小bit长度, 如UTF8的code unit是8, UTF16的code unit是16

UTF8

UTF8的code unit为8 bit

计算字符串在utf8编码的字节长度

JavaScript字符串是utf16编码, 常用的charAt(), getCharCode(), length都是基于utf16的code unit, 当字符在Unicode的Basic Multilingual Plane内时都可以正常工作, 计算utf8编码长度需要使用基于code point的函数, 自己手动实现编码转换肯定不现实, 应该使用对应的字符串处理库.

正则表达式也是基于code unit的.

平时我们用这些方法都没什么问题是因为平时接触到的字符都在Basic Multilingual Plane下面, Effective JavaScript中第七条: Think of Strings As Sequences of 16-bit code units有详细解释, 整本书也比较不错. 电子版我放到了附件中, 整本书没有废话, 都是干活, 强烈推荐

浏览器下的encodeURI, decodeURI, encodeURIComponent, decodeURIComponent刚好就是以code point工作的, 我们使用encodeURIComponent

因为encodeURIComponent不转义: 字母 数字 - _ . ! ~ * ‘ ( ) 需要把这部分先提取出来, 计算长度,

剩下的转义, 计算长度

var util = {
    /**
     * 计算字符串在utf8编码的字节长度
     * @param str {string} 需要计算字节长度的字符串
     * @return {number}  字符串在utf8编码下的字节长度
     **/
    utf8ByteLength: function (str) {
        str = str || ''
        var plainReg = /[\w-\.!~*'\(\)']/g
        var utf8TokenReg = /%\w{2}/g
        var plainTokens = str.match(plainReg) || []
        var complexStr = str.replace(plainReg, '')
        var encodedStr = encodeURIComponent(complexStr)
        var utf8Tokens = encodedStr.match(utf8TokenReg) || []
        return plainTokens.length + utf8Tokens.length
    }
}


console.log(util.utf8ByteLength('a'))   // 1
console.log(util.utf8ByteLength('¢'))   // 2
console.log(util.utf8ByteLength('中'))   // 3
console.log(util.utf8ByteLength('𐍈'))   // 4