0%

字符编码笔记(三)

Unicode

Unicode出现的目的就是为了解决国家地区之间字符编码互相不兼容的情况,所以尽可能多的收集已知的字符。

因为编码的字符很多,所以采用了类似GB2312分区的方式,不过这里的区称为平面,每个平面可以存放216(65536)个符号。

目前暂时有17个平面,所以总共有221个码位,需要3个字节,最常用的字符对应在最前面的65536码位,称为基本平面(BMP),剩下的称为辅助平面(SMP)。

Unicode规定了字符对应的码位,还规定了几种编码方式,就是不同的编码字符集转换字符编码表方式。

UTF-8

最通用的字符编码方式,晚于UTF-16出现。

采用变长编码,可以是1~3个字节。

编码规则如下:

  • 0x0~07F码位:编码为1个字节,和ASCII完全一致。
  • 0x80~0x7FF码位:编码为2个字节。使用原来码位填充格式:110xxxxx 10xxxxxx
  • 0x800~0xFFFF码位:编码为3个字节。使用原来码位填充格式:1110xxxx 10xxxxxx 10xxxxxx
  • 0x10000~0x10FFFF码位:编码为4个字节。使用原来码位填充格式:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

特点

  • 完全兼容ASCII
  • 节省空间
  • 第一个字节指明了码单元的所占的字节数
  • 后续每个字节都有固定的前缀
  • 码单元之间容易区分

UTF-16

基本平面内的码位编码为2个字节,其余使用4个字节。

对于基本平面(恰好是2字节)的码位,不足位在前端补充0即可。

对于其余平面码位,如果直接使用码位顺位下来的数字编码,会造成无法区分当前字节属于哪一种码单元(code unit)。

在基本平面的0xD800~0xDFFF,存在一片没有空余出来的码位,可以利用这个特点区分当前字节是否在是基本平面的编码字节,这段码位如果被占用,则应该是4个字节的其他平面字符编码。

上述空余码位区间被一份为2,形成了两个部分,0xD800~0xDBFF和0xDC00~0xDFFF,二进制形式分别是:0b1101 10HH HHHH HHHH0b1101 11L LLLL LLLL。这就形成了UTF-16中4字节编码的格式:0b1101 10HH HHHH 1101 11L LLLL,用其他平面的码位经过计算填充格式中的H和L即可。

计算方法

  • 码位减去0x10000,得到一个数字
  • 转为2进制,填充为20位
  • 前10位填充上述H位
  • 后20位填充上述L位

计算公式如下

1
2
3
// c为 其他平面的码位
H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c - 0x10000) % 0x400 + 0xDC00

辅助平面计算示例

1
2
3
4
5
6
7
8
9
str='A𠮷'
let tempStr1=str.codePointAt(0)
let tempStr2=str.codePointAt(1)
let tempStr3=str.codePointAt(2)
console.log(tempStr1)// 65
console.log(tempStr2)// 134071
console.log(tempStr3)// 57271
console.log(String.fromCodePoint(tempStr1))// A
console.log(String.fromCodePoint(tempStr2))//𠮷

这个字符串中,A处在基本平面内,𠮷处在辅助平面内,所以字符串是6个字节,长度是3。
tempStr2是获取到的𠮷码点,最后一行的输出可以验证这一点。
那么tempStr3是什么呢?是该字符串最后2个字节的值。可以验证一下。

前面知道了𠮷码点是134071,转换成16进制就是0x20bb7,根据UTF-16编码规则,首先减去0x10000,得到0x10bb7,二进制为0b0001 0000 1011 1011 0111,取后半部分11 1011 0111填充到低位格式中去,得到0b1101 1111 1011 0111,就是10进制的57271

1
2
3
4
5
6
7
8
// 接前一部分代码
let tempStr4=str.charCodeAt(0)
let tempStr5=str.charCodeAt(1)
let tempStr6=str.charCodeAt(2)

console.log(tempStr4)//65
console.log(tempStr5)//55362
console.log(tempStr6)//57271

tempStr5的值为什么是55362?因为字符串的第3、4字节存储的值就是它。接着上面说的验证步骤,提取0b0001 0000 1011 1011 0111的前10位填充进高位格式中去,得到0b1101100001000010,就是10进制的55362

UTF-32

最直接的编码方式,用4个字节表示所有码位。浪费存储空间。

UCS-2编码

UCS-2编码早于UTF-16出现,所以被许多编程语言首先支持。

UCS-2只支持基本平面的字符编码,是UTF-16的子集。