今天被人问到一个神奇的问题:你知道吗?'𠮷𠮷𠮷'.lenght !== 3?。当我第一次看到这个的时候,我忽然怀疑人生了?啥情况?难道穿越了?赶紧打开浏览器试一下
'𠮷𠮷𠮷'.lenght // 6
在仔细看一下这个字"𠮷",这是个啥?不是"吉"啊!这都不是一个汉字。也就是说它就不是一个字,那输出长度的时候为什么是这样子呢?去找了好多资料,才发现下面这个解释,这所有的问题都是因为编码格式引起的。这个"𠮷"被当作一个符号进行解析,但是它又不能使用一个字符表示,因为超出了识别范围,因此是两个字符,每一个的长度就是2.初看相当的抽象,接下来我就从计算机的基础知识展开讲讲吧。
常见的编码
- Unicode,是一个字符集,几乎为全世界已知的所有字符分配了一个编码,计算机只要支持这一个字符集,就能显示所有的字符,再也不会有乱码了。
- UTF-32,Unicode只规定了每个字符的码点,到底用什么样的字节序表示这个码点,就涉及到编码方法。最直观的编码方法是,每个码点使用四个字节表示,字节内容一一对应码点。这种编码方法就叫做UTF-32。
- UTF-8,是一种变长的编码方法,字符长度从1个字节到4个字节不等。
- UTF-16,与 UTF-8 类似,也是一种可变长度字符编码,其长度可能是 2字节 或者 4字节。基本平面的字符占用2个字节,辅助平面的字符占用4个字节。也就是说,UTF-16的编码长度要么是2个字节(U+0000到U+FFFF),要么是4个字节(U+010000到U+10FFFF)。
- UCS-2,UTF-16编码是 UCS-2 编码的超集,在没有辅助平面(UTF-16 的后2个字节)前,UTF-16 与 UCS-2 是指同一个东西,一个字符占用 2 个字节空间。而 JavaScript 就使用 UCS-2 编码,所以当字符码点大于 U+FFFF 时,需特别注意处理。
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为 2 个字节。对于那些需要 4 个字节储存的字符(Unicode 码点大于 0xFFFF 的字符),JavaScript 会认为它们是两个字符。
var s = "𠮷";
s.length // 2
s.charAt(0) // '\uD842'
s.charAt(1) // '\uDFB7'
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
上面代码中,汉字“𠮷”的码点是 0x20BB7,UTF-16 编码为 0xD842 0xDFB7(十进制为 55362 57271),需要 4 个字节储存。对于这种 4 个字节的字符,JavaScript 不能正确处理,字符串长度会误判为 2
为了解决这个问题,可以考虑一个简单粗暴的方式
Array.from('𠮷𠮷𠮷').length // 3
参考资料
字符串的扩展
Unicode与JavaScript详解