统一码编码详情(四种):UTF-16 编码
Unicode编码详解(四):UTF-16编码
本文是对《C++语法详解》一书相关章节的增补,以增强读者对字符的理解,因为《C++语法详解》引用的标准过于老旧。
《C++语法详解》网盘地址:
https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg
本文摘自本人所作《Unicode编码和双向算法(bidi)详解》网盘地址
链接:https://pan.baidu.com/s/1LLKv22jQPmeba1XUCm0xoQ?pwd=a3x8
提取码:a3x8
有兴趣的读者可参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。
由于本人能力有限,文中难免有错漏之处,望广大读者指出更正,不胜感激
注意:若对本文的专业术语不了解,请参阅本系列文章(一)和(二)
一、UTF-16编码简介
1、
UTF-16是变长编码方式,每个字符编码为2或4字节。
2、历史
- UTF-16编码源于UCS-2,是Unicode最早的编码方式。
- UCS-2编码仅覆盖了基本平面(即BMP,第0平面)中的码点,使用固定的两字节将字符编号(类似于Unicode中的码点值)直接映射为字符编码,中间未经过任何的编码算法转换。
- 很明显,16位的二进制位(范围为0x0000 ~ 0xFFFF)无法表示Unicdoe引入的增补平面中的码点(平面1 ~ 16,码点范围为0x10000~0x10FFFF),为此,Unicode在UTF-16编码中使用“代理(代替)机制”来解决这个问题,代理机制使用4个字节来表示增补平面中的码点,从而使UTF-16成为一种变长编码方式。
- 因此,若软件仅支持UCS-2编码,则意味着仅支持UCS字符集或Unicode字符集基本平面中的字符,而不支持增补平面中的字符。
二、代理(代替)机制与UTF-16编码规则
1、代理机制的基本思想
代理可简单理解为代替,其基本思想就是使用两个基本平面中未定义(或未使用)的码点合起来代替一个增补平面的码点。
2、代理机制的基本规则
- 基本平面中的字符(除U+D800~U+DFFF外)仍然使用固字两字节直接映射的方式编码,即,基本平面字符的字符编号(码点值)就是字符编码。
- 增补平面的字符使用“代理机制”这一编码算法进行编码,使用代理机制后需要使用4字节来表示Unicode增补平面中的码点,因此,UTF-16也是一种变长编码方式。
3、代理对和代理区
-
代理对是指基本平面中用来代替增补平面码点的两个码点,也就是说,代理对是一对(即,两个)码点值。
-
代理区
-
- 基本平面中用来代替增补平面码点的未使用的码点区域被称为代理区,其码点范围为0xD800 ~ 0xDFFF,共2048个码点,
-
- 代理区又分为高代理码点(或称为高代理码元,范围为0xD800 ~ 0xDBFF)和低代理码点(或称为低代理码元,范围为0xDC00 ~ 0xDFFF),图3为代理区的分类。
-
- 需要注意的是,代理区中的码点都未定义字符,这些码点主要用于代理增补平面中的码点。假设为代理区中的码点指定了字符,那么一个代理区中的码元,到底是表示基本平面的字符呢还是增补平面中的字符的代理码元呢?这就产生了冲突,所以,为避免冲突,代理区中的码点都未定义字符。
- 需要注意的是,代理区中的码点都未定义字符,这些码点主要用于代理增补平面中的码点。假设为代理区中的码点指定了字符,那么一个代理区中的码元,到底是表示基本平面的字符呢还是增补平面中的字符的代理码元呢?这就产生了冲突,所以,为避免冲突,代理区中的码点都未定义字符。
4、UTF-16编码规则
把高代理码点和低代理码点合起来组成“代理对”,使用代理对来代替增补平面的码点,代理对刚好可完全表示增补平面内的所有码点(原因见后文)。比如,增补平面的第一个码点0x10000的UTF-16编码是0xD800 DC00,同理,第二个码点0x10001的UTF-16编码是0xD800 DC01,其余以此类推。
三、UTF-16编码方法
1、方法1:查表
根据UTF-16的编码规则可制出如表6所示的一个表格,然后通过查表的方式查找增补字符的编码,但这种方法非常麻烦,实际编码时并不使用。
2、方法2:计算法
①、将增补字符的码点值减去0x10000,得到一个20位长的二进制数
②、将得到的20位长二进制数拆分为高10位比特和低10位比特
③、20位长的高10位比特加上0xD800得到第一个代理码点,即高代理码点
④、20位长的低10位比特加上0xDC00得到第二个代理码点,即低代理码点
⑤、将得到的高代理码点和低代理码点组合成“代理对”,便得到了增补字符的UTF-16编码
⑥、示例:求增补平面码点值为U+10437的UTF-16编码
-
将0x10437减去0x10000,得到0x00437,二进制为0000 0000 0100 0011 0111
-
将高10位,即0000 0000 01加上0xD800(二进制为1101 1000 0000 0000),得到高代理码点为:0xD801(二进制为1101 1000 0000 0001)
-
将低10位,即00 0011 0111加上0xDC00(二进制为1101 1100 0000 0000),得到低代理码点为:0xDC37(二进制为1101 1100 0011 0111)
-
将高代理码点和低代理码点组合成代理对得到UTF-16编码为0xD801 DC37
3、方法3:二进制位填补法
①、原理
高代理码点的起始值0xD800(二进制为1101 1000 0000 0000)和低代理码点的起始值0xDC00(二进制为1101 1100 0000 0000),可发现,他们的前6位分别为110110和110111,而后10位都是0,总共有20位(220=1048576),刚好可表示增补平面中的所有码点(0x10000 ~ 0x10FFFF,共220=1048576个码点),若利用代理码点的这20位来表示增补码点,则高代理码点的范围为1101 1000 0000 0000(0xD800) ~ 1101 1011 1111 1111(0xDBFF),低代理码点的范围为1101 1100 0000 0000(0xDC00) ~ 1101 1111 1111 1111(0xDFFF),刚好在高代理码点和低代理码点的范围内。因此,可利用填补二进制位的方法来编码UTF-16
②、步骤1:
将高代理码点和低代理码点分别展开为如图4所示的形式,其中高代理单元的110110和低代理单元的110111是固定不变的数(定数),p和x是变数,去掉定数后组合起来就是pppp xxxx xxxx xxxx xxxx,共20位,刚好可表示全部增补码点,其中pppp表示16个增补平面的编码(24=16),紧接着的16个x表示某个增补平面内的码点。
③、步骤2、
将增补字符的码点值减去0x10000,得到一个20位长的二进制数
④、步骤3、
将得到的20位长二进制数依次填补步骤1中的变数便得到UTF-16的编码
⑤、示例:
求增补平面码点值为U+10437的UTF-16编码
- 将0x10437减去0x10000,得到0x00437,二进制为0000 0000 0100 0011 0111
- 将0000 0000 0100 0011 0111依次填补高代理单元和低代理单元中的变数(如图5所示),得到其编码为0xD801
DC37
四、字节序问题
UTF-16编码后的码元序列在映射为物理意义上的字节序列时,又分为UTF-16BE (大端序),UTF-16LE (小端序)两种情况,大端序和小端序又分为带有字节序标记(with BOM)和不带字节序标记(without BOM)两种情形。比如,“ABC”这三个字符的UTF-16编码(码元序列)为:00 41 00 42 00 43;其对应的各种字节序列如表7所示:
五、在记事本中使用UTF-16编码
六、UTF-16编码的优缺点及特点
九、Unicode编码方式总结
代码页、内码、外码、汉字编码简介
本文作者:黄邦勇帅(原名:黄勇)
下一篇: 从内存管理角度谈 MMU
推荐阅读
-
位、字节、WORD、DWORD 的区别和联系 - Unicode 和 ANSI 的区别就像输入法中 "全宽 "和 "半宽 "的区别一样。 由于不同的 ANSI 编码有不同的标准(不同的字符集),对于给定的多字节字符串,我们必须知道它使用的是哪种字符集,才能知道它包含哪些 "字符"。对于 UNICODE 字符串来说,无论环境如何,它所代表的 "字符 "内容始终是相同的。Unicode 有一个统一的标准,定义了世界上大多数字符的编码,因此拉丁文、数字、简体中文、繁体中文和日文都可以存储在一个编码中。统一码是一个统一的标准,定义了世界上大多数字符的编码。 比特(Bit)和字节(Byte)的区别:例如USB2.0 标准接口的传输速率为 480Mbps,有一些人误认为是每秒 480 兆比特,同样网络带宽为 2MB,就容易误认为是每秒 2 兆比特。其实,480Mbps 应该是 480 兆比特/秒或 480 兆字节/秒,它等于 "60 兆字节/秒";同样,2MB,应该是 256 兆字节/秒。 Bit 和 Byte 译为 "比特",都是数据计量单位,比特="位 "或 "比特"。 Byte = 字节,即 1byte = 8bits,两者的换算关系为 1:8。 Mbps = mega bits per second(兆位/秒)是速率单位,因此 2M 带宽应为 2 兆位/秒,即 2MBps。MB = 兆字节(Megabytes,兆字节)是单位量,1MB/S(兆字节/秒)= 8MBPS(兆字节/秒)。 通常所说的硬盘容量是指 40GB、80GB、100GB,其中的 B 是指 Byte 也称为 "字节"。 1 KB=1024 字节 1 MB=1024 KB=1024*1024 字节 1 GB=1024 MB=1024*1024*1024 字节 例如,以前所谓的 56KB MODEM 转换过来的 56KBps 除以 8 就是 7Kbyte,所以真正从网上下载文件存在硬盘上的速度也是每秒 7Kbyte;也就是说,用 B 表示传输速度一般指 Bit;用 B 表示容量一般指 Byte。比特、字节、WORD、DWORD 的本质。
-
与汉字编码和编程有关的问题摘要:ASCII 码、机器码、区域码、国家标准码、统一码及其转换方式。
-
统一码编码详情(四种):UTF-16 编码
-
通用统一码编码
-
统一码和 GB2312 编码表,并转换为 ASCII 字符
-
Python 编码及运算符详细讲解-在计算机硬件中,编码(coding)是指用代码来表示各组数据资料,使其成为可利用计算机进行处理和分析的信息。代码是用来表示事物的记号,它可以用数字、字母、特殊的符号或它们之间的组合来表示。 2.编码的种类(常用种类) ①ASCCI 1.ASCCI的产生 在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)、以及1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示。 2.ASCCI的表述 ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到标点符号, 以及在美式英语中使用的特殊控制字符。 字母A用ASCII编码是十进制的65,二进制的01000001; ②unicode 1.Unicode的产生