彻底搞懂字符编码( 二 )


 
缺点:
1.对于中日韩的语言,一个字符需要三个字节表示,占用空间大 。
2.计算长度效率低,由于是变长的,所以在计算字符串长度的时候执行效率比较低 。
 
UTF-16UTF-16也是经常用到的编码方式,同样它也是可变长的编码方式,下图是编码规则,字符长度2个字节或者4个字节表示一个字符 。

彻底搞懂字符编码

文章插图
 
随即就有了一个问题,当我们遇到两个字节,怎么看出它本身是一个字符,还是需要跟其他两个字节放在一起解读形成一个字符?
U+D800到U+DFFF是一个空段,这些码点不对应任何字符,编号大于U+0FFFF的字符,一半在U+D800到U+DBFF之间,一半在U+DC00到U+DFFF之间,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起形成一个字节 。刚好可以涵盖辅助面的字符 。
 
优点:
1.由于是固定2个字节和4个字节,所以在计算字符串长度、执行索引操作时速度很快 。
缺点:
1.UTF-16 能表示的字符数有 6 万多,但是实际上目前 Unicode 5.0 收录的字符已经达到 99024 个字符,早已超过 UTF-16 的存储范围 。
2.UTF-16 存在字节序问题(大端和小端)使用时需要提前协定好 。
3.容错性差,因为存在字节序的问题,如果中间某一个字节丢失时可能会导致后面的解码错误 。
UTF-324 个字节表示一个代码值,固定长度,多出来的部分前面补0,这种编码方式占空间较多,使用场景很少 。
字节序字节序是指数据在存储器中的存放顺序,分为大端和小端两种 。之所以会存在字节序的问题是因为寄存器的长度要大于一个字节,不同的操作系统读取字节的顺序不一样,
大端模式,是指数据的高字节在前,保存在内存的低地址中,与人类的读写法一致,数据的低字节在后,保存在内存的高地址中,文件前缀FE FF 。(mac OS是大端模式)
小端模式,是指数据的高字节在后,保存在内存的高地址中,而数据的低字节在前,保存在内存的低地址中,文件前缀FF FE 。(x86和一般的OS(如windows,FreeBSD,linux)使用的是小端模式) 。
彻底搞懂字符编码

文章插图
 
所以UTF-16存在一个字节序的问题,需要在文件前面声明,而UTF-8不存在这个问题,原因在于UTF-8的最小编码单位是1个字节,不会存在两个字节谁在高位谁在地位的问题 。
还是以汉字“严”为例,Unicode 码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25 。存储的时候,4E在前,25在后,这就是 Big endian 方式(4E 25) 。25在前,4E在后,这是 Little endian 方式(25 4E) 。
 
用途UTF-8,广泛用于数据存储及传输:例如html文档中的<meta charset="UTF-8">,以及Python文件当出现中文的时候会在顶部加上“# coding: UTF-8”等 。
UTF-16,而一些流行语言比如Java、JavaScript、Python、Objective-C等字符串内部字符串都用UTF-16编码.在计算字符串长度搜索是的效率较好 。
 
Objective-C中的NSString
彻底搞懂字符编码

文章插图
 
Java中的String类
彻底搞懂字符编码

文章插图
 
实践1.日常字母,汉字,表情分别用UTF-8和UTF-16表示分别用多少字节?
字母UTF-8用一个字节,UTF-16用两个字节 。
大部分的汉字UTF-8编码后由三个字节如下图的“严”是e4b8a5,而用UTF-16编码仅用2个字节即4e25 。
大部分表情等特殊符号UTF-8编码后占4个字节,UTF-16编码后也占4个字节 。
彻底搞懂字符编码

文章插图
 
2.字符计算长度length方法是怎么计算的?
像NSString,java,javascript等语言,由于是UTF-16编码的,在计算长度的时候由总字节/2得来的 。
3.实际开发时,如果想计算字符实际长度该怎么计算?
1)一种是通过判断码点所在位置进行判断,只要落在0xD800到0xDBFF的区间,就要连同后面2个字节一起读取.
var index = -1;var string = '12';var length = string.length;var output = [];while (++index < length) {var charCode = string.charCodeAt(index);var character = string.charAt(index);if (charCode >= 55296 && charCode <= 56319) {output.push(character + string.charAt(++index));} else {output.push(character);}}console.log(output) //["", "1", "2"]consolo.log(0xD800 ===55296) // true


推荐阅读