字符集相关的概念
字符集&编码
- charset 是 character set 的简写,即字符集。
- encoding 是 charset encoding 的简写,即字符集编码,简称编码。
编号 编码
- 字符 <–> 编号 <–> 编码
- 编号不涉及具体使用多少字节来表示、是用定长还是变长方案等细节问题。编号仅仅是一个抽象的概念,将具体字符映射到一个唯一的code上,是把字符数字化的一个过程。
- 字符集是一组映射关系[字符 - 编号] ,即对字符的数字化抽象,但不体现具体的编码转换格式。如Unicode就是一个字符集。
- 编码是编号的具体存储方式,将抽象的编号转化成具体的编码 - 编码对应[存储字节数、是否变长...]
下面具体以将Unicode展开讲解
Unicode字符集
- Unicode 是 Unicode Standard(Unicode标准)的简写,所以 Unicode 即是指 Unicode 标准。
- 它脱离具体平台、语言,给每一个字符 一个唯一的数字 -- 码点(code point)
码点的格式 U+[XX]XXXX X:代表一个十六制数字
可以有 4-6 位,不足 4 位前补 0 补足 4 位,超过则按是几位就是几位。具体示例:
- U+0048(不足 4 位前补 0 )
- U+4F60
- U+1D11E(超过4位则按实际位数)
范围[U+0000,U+10FFFF] 理论大小为 10FFFF+1=110000(16进制) 是一个百万级别的数。
10FFFF + 1 = 110000(16) = (16 + 1) * FFFF = 17 * 16^4 = 17 * 65536(10)
可以拆分出 17 个 范围为FFFF的平面
平面[Plane]为了更好分类管理如此庞大的码点数,把每 65536 个码点作为一个平面,总共 17 个平面。
- 第一个平面称为 BMP(Basic Multilingual Plane 基本多语言平面),也叫 Plane 0,它的码点范围是 U+0000 ~ U+FFFF
- 后续的 16 个平面称为 SP(Supplementary Planes)。范围超过了 U+FFFF 即超过了 2个byte = 16 bit (2^16)个的理论上限
CJK 统一汉字(CJK:Chinese, Japanese, and Korean,中日韩)在first plane 中间很大一片区域
流传配置汉字的正则表达式 [\u4E00-\u9FA5] 就是plane 0 中这个范围的 Unicode值,严格来说是不准确的。
代理区 BMP中还有一片空白,这段空白从 D8~DF。其中前面 D800–DBFF 属于高代理区(High Surrogate Area),后面的 DC00–DFFF 属于低代理区(Low Surrogate Area),各自的大小均为 4×256 (2^8) = 1024
这两个区组成一个二维的表格,共有1024×1024=210×210=24×216=16×65536,所以它恰好可以表示增补的 16 个平面中的所有字符。
高代理称为Lead,低代理位Trail,一高一低即是一个代理对
映射关系,有图表示
例如:
(D8 00 DC 00)—>U+10000,左上角,第一个增补字符
(DB FF DF FF)—>U+10FFFF,右下角,最后一个增补字符
Unicode的具体编码格式
三种编码方案的定变长与字节数总结:
- UTF-8 :变长,1-4 字节;
- UTF-16:变长,2 或 4 字节;
- UTF-32:定长,4 字节。
定长与变长
- 在容量 和 效率之间全行,出现了变长的编码方案。
- 变长的设计核心在于区分不同的变长字节:利用高位区分,利用代理区区分
- 默认标示位(高位来区分)的变长方案,损失了很多有效码位
- 代理区做区分的方案(保留一部分code【70-80】排列组合,映射出70*80 100个code)
具体编码实现
UTF 即是 Unicode 转换格式(Unicode (or UCS) Transformation Format)
UTF-32
- 码点最大的 10FFFF 占 21 位,UTF-32 采用的定长4 byte 则是 32 位。高位补 0 的形式补够 32 位即可,缺点占用空间太大。
- 4字节 XXXX XXXX , XXXX XXXX , XXXX XXXX , XXXX XXXX
UTF-8
- 变长的编码方案:1,2,3,4 四种 byte 组合
采用高位保留方式 来区别不同变长
- 1字节 0XXX XXXX (7)
- 2字节 110X XXXX 10XX XXXX ) (11)
- 3字节 1110 XXXX 10XX XXXX 10XX XXXX (16)
- 4字节 1111 0XXX 10XX XXXX 10XX XXXX 10XX XXXX (21)
码点对应字节
- 编码转二进制数,去掉高位0 判断采用几字节
- 一字节 有效位7位(2^7 = 128)兼容ASCII,[码点] U+0000 ~ U+007F (0~127)
二字节 11位有效,2^11 = 2048 个编码空间 码点 U+0080 ~ U+07FF (128~2047)
- 因为去掉了一字节的码点,二直接码点从 128~2047,所以不会占满 2048 个编码空间,是有冗余的,下面同样如此
三字节 16位有效,65536 个编码空间 码点U+0800~U+FFFF (2048~65535)
- 同样三字节码点范围 U+0800~U+FFFF(2048~65535)
- 存着绝大部分汉字(常用字),还有很多偏门汉字保存在增补平面中
- 四字节剩余 21位有效位数,最大的Unicode码点 10FFFF(1 + 4 * 5) 也是 21 位,刚好囊括。U+FFFF 以上的增补平面的字符都在这里表示
- 按照 UTF-8 的模式,它还可以扩展到 5 字节,乃至 6 字节变长,但 Unicode 说了码点就到 10FFFF,不扩充了,所以 UTF-8 最多到四字节就足够了
码点转换为UTF-8编码
- 将码点转换成 2进制
- 将二进制数 按照UTF-8 固定位分组
- 选中几字节模式后,高位不足 补0
重新将 2进制数 转换成16进制数
汉字“你” (U+ 4F60)
转2进制 :U+ 4F60 -> 0100 1111 0110 0000 (16位 按照3字节转换)
分组 :0100 1111 0110 0000 -> 0100 1111,01 10,0000 (3字节 4+6+6)
替换有效位:1110 0100 1011 1101 1010 0000
按字节转16进制:E4 BD A0
- UTF-8 三字节模式固定了 1110 的开头模式,所以多数汉字总是以 1110 开头,换成 16 进制形式,1110 就是字母 E。
如果看到一串的 16 进制有如下的形式:EX XX XX EX XX XX…
每三个三个字节前面都是 E 打头,那么它很可能就是一串汉字的 UTF-8 编码了
UTF-16
- BMP中的码点,直接对应无需转换 (plane 0 范围2byte = 16bit,16位刚好对应)
变长的 2 或 4 字节编码模式
- BMP内的字符使用 2 字节编码
- 其它的则使用 4 字节组成所谓的代理对来编码
代理区
- 鸟瞰图中,一片空白的区域,就是代理区(Surrogate Area),
- 为了编码增补平面中的字符而保留的,总共有 2048 个位置,均分为高代理区(D800–DBFF)和低代理区(DC00–DFFF)两部分,各1024,这两个区组成一个 二维的表格,共有:
1024 × 1024 = 2^10×2^10 = 2^4×2^16 = 16×65536
- 它恰好可以表示增补的 16 个平面中的所有字符
码点转换
- BMP 中直接对应,无须做任何转换
增补平面 SP 中:
- 减去10000(16进制)[第一个平面中的所有码点]
- 除以代理区的行宽[ 1024(10进制) ] 商:第几个高代理区 余数:第几个低代理区
- Lead = (码点 – 10000(16)) ÷ 40016 + D800
Trail = (码点 – 10000(16) % 40016 + DC00
- 实际转换用API, 通过位移运算更高效