从0开始构建自己的前端知识体系-有趣的unicode
前言
最近在做表单需求的时候遇到了emoji的问题,发现自己对字符编码上的理解还是过于浅显,于是翻阅资料,弥补缺陷。
What is unicode
简单来讲,unicode
就是一个机器语音与人类语言的一张对应表。
可以理解为一个很大很大的map
,唯一的key
对应唯一的字符。
重要理念
- 码点
码点指的是
unicode
这个map
里的每一个key
。例如
\u004a
对应的是J
这个字符,那么J
的码点就是74
- unicode与编码
编码是对码点进行的编排,我们日常说的UTF-8, UTF-16, UTF-32都是编码方式。不同的编排方式有不同的优势与作用。
码点范围
- 基础平面
码点从0x0000 - 0xFFFF,共可存放65536个字符
- 辅助平面
码点从0x010000 - 0x10FFFF,共可存放16 * 65536个字符,可以划分为16个不同的平面
- 基础平面
- 码点
故我们可以认为目前unicode码表的码点最多可以用4个字节来表示
- history
Ascii码大家都了解,128个字符,1个字节完全可以一一对应。但是计算机不是光美国使用,全世界人民都想用计算机,都想在计算机上显示出自己的语言。于是就对这1个字节的剩余128个空间开始搞事情。
那么问题来了,中国文化博大精深,128个空间对于中国文字来讲,简直不够塞牙缝。于是国人自己搞了一套GBK码表用两个字节就能表示65536个字符了,暂时解决了一下问题。
但是码表与编码不统一,必然会导致两个计算机解码显示上的差异,也就造成了所谓的乱码。
于是unicode的目的,就是为了建立一张能容纳全人类各种文化的字符的码表,来统一互联网的字符规范。
unicode 编码规则
通过编码规则可以对字符进行传输,通过解码后的码点与unicode表对照可以正确的显示文字
UTF-32
一种很简单的定长编码方式,均为4个字节编码
- 优点
查找复杂度为O(1)
- 缺点
浪费空间,传输内容只有ASCII时会是其的4倍。
- 优点
故在网络传输中,传输内容的大小决定了流量与客户体验,这种编码方式并不适合互联网。
UTF-16
一种变长的编码方式,采用2个字节来表示基础平面,4个字节来表示辅助平面
编码范围 字节 0x0000 - 0xFFFF 2 0x010000 - 0x10FFFF 4 - question
那么在编码与解码过程中,是如何区分应该是2字节还是4字节呢?
- answer
我们知道,辅助平面的字符共有 16 * 65536个,也就是 2^20个。
在基本平面内,码点从0xD800到0xDFFF是一个空段,不对应任何字符。那么正好从0xD800-0xDBFF空间大小为1024(2^10),从0xDBFF-0xDFFF空间大小为1024(2^10)。于是就可以把辅助平面的码点分为高低位映射在这两个码点范围内,正好可以存储所有的辅助平面字符。
也就是说只要超过0xFFFF的码点,都会被编码成高位在0xD800-0xDBFF,低位在0xDC00-0xDFFF的4字节码点。这样解码的时候还是依次读取2字节,遇到0xD800-0xDBFF就知道这是个辅助平面字符,再读取2字节去解码
转码公式
- 如果是基本平面字符,直接将码点转为对应的16进制形式,长度为2字节
- 如果是辅助平面字符,则使用转码公式
H = Math.floor((c-0x10000) / 0x400)+0xD800 L = (c - 0x10000) % 0x400 + 0xDC00
- question
UTF-8
一种变长的编码方式,越常用的字符编码长度越短
- question
如何区分到底应该读取几个字节来编码解码呢?
answer
编码范围 编码方式 字节 0x0000 - 0x007F 0xxxxxxx 1 0x0080 - 0x07FF 110xxxxx 10xxxxxx 2 0x0800 - 0xFFFF 1110xxxx 10xxxxxx 10xxxxxx 3 0x010000 - 0x10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4 编码规则:
- 对于码点在0-127的,用1字节,第一位置0,后面按码点对应,其实就与ASCII相同了
- 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
- question
由于UTF-8编码这种变长的编码规定,极大地节省了网络传输数据的空间,传输常用的ASCII码不用占据额外的空间,提升了效率
JavaScript编码规则
有趣的是,js与采用的是UCS-2
编码方法。
UCS-2
是UTF-16
编码的子集,只支持2字节解析,从而就导致了4字节字符被当做2个2字节字符解析,字符函数等就会出现问题。
question
// '