1.

UTF-16编码方式源于UCS-2(Universal Character Set coded in 2 octets、2-byte Universal Character Set)。而UCS-2,是早期遗留下来的历史产物。

UCS-2将字符编号直接映射为字符编码(CEF,而非CES,详见前文中对现代字符编码模型的解释),亦即字符编号就是字符编码,中间没有经过特别的编码算法转换。因此,从现代字符编码模型的角度来看的话,此时并没有将编号字符集CCS与字符编码方式CEF作严格区分,既可以将UCS-2看作是编号字符集CCS中的字符编号,也可以看作是字符编码方式CEF中的字符编码。

后来,随着Unicode联盟与ISO/IEC就创建全球统一的单一通用字符集进行合作,Unicode字符集与UCS字符集逐渐相互融合,两者最终基本保持了一致(详见前文《刨根究底字符编码之八——Unicode编码方案概述》中的介绍)。

(笨笨阿林原创文章,转载请注明出处)

2.

这之后,Unicode逐渐占据了主导地位,并引入了UTF-16编码方式。为什么要引入UTF-16编码方式呢?

前文已经介绍过了,Unicode字符集(CCS)到目前为止定义了包括1个基本平面BMP和16个增补平面SP在内的共17个平面。

每个平面的码点数量为2^16=65536个,因此17个平面的码点总数为共65536*17=1114112个。其中,基本平面码点为65536个(码点编号范围为0x0000~0xFFFF),增补平面码点为1114112-65536=65536*16=1048576个(码点编号范围为0x10000~0x10FFFF)。

很明显,简单地用一个16位码元肯定无法表示所有17个平面的这么多码点(因为2^16=65536,而码点总数为65536*17=1114112)。而UCS-2,正是用两个字节共16位来表示一个字符的。为支持字符编号超过U+FFFF的增补字符,扩展势在必行。

3.

UCS因而又提出了UCS-4,即用四个字节共32位来表示一个字符(此时UCS-4同样既可认为是编号字符集CCS中的字符编号,也可认为是字符编码方式CEF中的字符编码)。但码元也因此从16位扩展到了32位。

而Unicode却提出了不同的扩展方式——代理机制。具体而言,就是为了能以一个统一的16位码元同时编码基本平面以及增补平面中的字符码点编号,Unicode设计引入了UTF-16编码方式,并且通过代理机制实现了扩展。

UTF-16编码方式的引入,从现代字符编码模型的角度来看的话,彻底将编号字符集CCS与字符编码方式CEF作了严格区分。也就是说,在UTF-16编码方式中,编号字符集CCS中的字符编号与字符编码方式CEF中的字符编码不再仅仅是简单的直接映射关系。

具体来说,就是Unicode字符集基本平面BMP中的字符(大致相当于UCS字符集中的UCS-2字符,但必须除开U+D800~U+DFFF这一在Unicode字符集BMP中称之为代理码点的部分),仍然是直接映射关系,亦即这部分字符的字符编号与字符编码是等同的。

但Unicode字符集增补平面中的字符(大致相当于UCS字符集UCS-4字符中除开UCS-2字符的部分,因为广义上的UCS-4字符实际上包含了UCS-2字符,当然狭义上的UCS-4字符不包括UCS-2字符),却不是直接映射关系,而是必须通过代理机制这一编码算法的转换,亦即这部分字符的字符编号与字符编码不是等同的。

因此,在Unicode引入了UTF-16编码方式之后,站在现代字符编码模型的角度上来看的话,再将UCS-2和UCS-4直接称之为字符编码方式CEF已不是很合适,更多的应该是编号字符集CCS中的概念(当然,在了解其历史原因之后,将UCS-2和UCS-4同时理解为编号字符集CCS和字符编码方式CEF也未尝不可);而若将UCS-2等同于UTF-16,将UCS-4等同于UTF-32(后文会有介绍),显然也是不合适的。

(笨笨阿林原创文章,转载请注明出处)

4.

UTF-16中的所谓代理机制,实际上就是用两个对应于基本平面BMP代理区(Surrogate Zone)中的码点编号的16位码元来表示一个增补平面码点,这两个用来表示一个增补平面码点的特殊16位码元被称之为代理对(Surrogate Pair)(解释详见后文《UTF-16究竟是如何编码的——UTF-16的编码算法详解》)

UTF-16编码方式及其代理机制是在Unicode 2.0中为支持字符编号超过U+FFFF的增补字符而引入的,于是从此就由UCS-2的等宽(16位)码元序列编码方式(如前文所述,从现代字符编码模型的角度来看的话,UCS-2更多是的编号字符集CCS中的概念,但考虑到其历史原因,称之为字符编码方式CEF亦未尝不可,下同,不再赘述),变成了UTF-16的变宽(16位或32位)码元序列编码方式。不过,码元依然保持了16位不变。

5.

UCS-2所编码的字符集中的U+D800~U+DFFF这部分代理码点除外的话,UTF-16所编码的字符集可看成是UCS-2所编码的字符集的父集。

在没有引入增补平面字符之前,UTF-16与UCS-2(U+D800~U+DFFF这部分代理码点除外)的编码完全相同。但当引入增补平面字符后,UTF-16与UCS-2的编码就不完全相同了(事实上,由于UCS-2只有两个字节,根本无法编码增补平面字符)。

现在若有软件声称自己支持UCS-2编码,那相当于是在暗示其仅支持UCS字符集或Unicode字符集中的基本平面字符,而不能支持增补平面字符。

6.

所以说,UTF-16是变长编码方式,每个字符编码为16位或32位;而UCS-2是定长编码方式,每个字符编码固定为16位。但两者的码元却都是16位的(而UTF-32和狭义的UCS-4的码元都是32位的)。

另外,UTF-16中,大部分汉字采用两个字节编码,少量不常用汉字采用四个字节编码。

Windows 2000及之后的版本是支持UTF-16的,之前的Windows NT/95/98/ME是只支持UCS-2的。

(笨笨阿林原创文章,转载请注明出处)

7.

作为逻辑意义上的UTF-16编码(码元序列),由于历史的原因,在映射为物理意义上的字节序列时,分为UTF-16BE(Big Endian)、UTF-16LE(Little Endian)两种情况。比如,“ABC”这三个字符的UTF-16编码(码元序列)为:00 41 00 42 00 43;其对应的各种字节序列如下:

Windows平台下的UTF-16编码(即上述的FF FE 41 00 42 00 43 00) 默认为带有BOM的小端序(即Little Endian with BOM)。你可以打开记事本,写上ABC,保存时选择Unicode(这里的Unicode实际上指的是UTF-16 Little Endian with BOM,即带BOM的UTF-16小端序CES编码,详见后文解释)

然后保存,再用UltraEdit编辑器看看它的编码结果:

Windows从NT时代开始就采用了UTF-16编码方式,很多流行的编程平台,例如.Net、Java、Qt还有Mac下的Cocoa等都是使用UTF-16作为基础的字符编码。例如代码中的字符串,在内存中相应的字节流就是UTF-16字节序列的。(注意,UTF-16编码在Windows环境中被误用为“widechar”和“Unicode”的同义词)

8.

UTF-16一方面使用变长码元序列的编码方式,相较于定长码元序列的UTF-32算法更复杂(甚至比同样是变长码元序列的UTF-8也更为复杂,因为引入了独特的代理对这样的代理机制);另一方面仍然占用过多字节,比如ASCII字符也同样需要占用两个字节,相较于UTF-8更浪费空间和带宽。

因此,UTF-16在Unicode字符集的三大编码方式(UTF-8、UTF-16、UTF-32)中表现较为糟糕。它的存在是历史原因造成的,引起了很多混乱。不过由于其推出时间最早,已被应用于大量环境中,目前虽然不被推荐使用,但长期来看,作为程序人员都不得不与之打交道。因而,对于其具体的编码算法的了解是十分必要的,本系列文章的下一篇将详细介绍其复杂的编码算法(主要是代理编码算法)。

(笨笨阿林原创文章,转载请注明出处)

(未完待续)

【转】刨根究底字符编码之十三——UTF-16编码方式相关推荐

  1. 【转】刨根究底字符编码之十一——UTF-8编码方式与字节序标记BOM

    一.UTF-8编码方式 1. 接下来将分别介绍Unicode字符集的三种编码方式:UTF-8.UTF-16.UTF-32.这里先介绍应用最为广泛的UTF-8. 为满足基于ASCII.面向字节的字符处理 ...

  2. 【转】刨根究底字符编码【2.0版】(1):开篇

    首先跟大家分享一个有趣的亲身经历.有一次,在网上我看到有程序员发了一个帖子,帖子题目乍一看让人感到惊愕,但细一想又让我会心一笑. 这个帖子的题目大致上是这样的:字符编码是不是让程序员最感到恶心的问题? ...

  3. 【转】刨根究底字符编码之十——Unicode字符集的字符编码方式

    一.字符编码方式CEF的选择 1. 由于Unicode字符集非常大(并且作为开放字符集还在不断扩展之中),有些字符的编号(即码点值)需要两个或两个以上字节来表示,而要对这样的编号进行编码,也必须使用两 ...

  4. 【转】刨根究底字符编码之九——字符编码方案的演变与字节序

    字符编码方案的演变与字节序 一.字符编码方案的演变 1. 根据前面的介绍,对于字符编码方案的演变,我们大致上可简单地划分为三个阶段: ① ASCII编码方案阶段 → ② ANSI编码方案阶段 → ③ ...

  5. 刨根究底字符编码之六——简体汉字编码中区位码、国标码、内码、外码、字形码的区别及关系

    简体汉字编码中区位码.国标码.内码.外码.字形码的区别及关系 GB2312.GBK.GB18030等GB类汉字编码方案的具体实现方式是怎样的?区位码是什么?国标码是什么?内码.外码.字形码又是什么意思 ...

  6. 刨根问底:C++中宽字符类型(wchar_t)的编码一定是Unicode?长度一定是16位?

    转发的:https://www.ituring.com.cn/article/111027的文章 刨根问底:C++中宽字符类型(wchar_t)的编码一定是Unicode?长度一定是16位? cons ...

  7. 【转】刨根究底字符编码之十六——Windows记事本的诡异怪事:微软为什么跟联通有仇?

    1. 当用一个软件(比如Windows记事本或Notepad++)打开一个文本文件时,它要做的第一件事是确定这个文本文件究竟是使用哪种编码方式保存的,以便于该软件对其正确解码,否则将显示为乱码. 一般 ...

  8. 【转】刨根究底字符编码之十四——UTF-16究竟是怎么编码的

    1. 首先要注意的是,代理Surrogate是专属于UTF-16编码方式的一种机制,UTF-8和UTF-32是不用代理的. 如前文所述,为了让UTF-16能继续编码基本平面后面的增补平面中的码点值,于 ...

  9. 【转】刨根究底字符编码之十二——UTF-8究竟是怎么编码的

    UTF-8究竟是怎么编码的 1. UTF-8编码是Unicode字符集的一种字符编码方式(CEF),其特点是使用变长字节数(即变长码元序列或称变宽码元序列)来编码.目前一般是1到4个字节,当然,也可以 ...

最新文章

  1. 【HDU】1284 钱币兑换问题 (想一想)
  2. d3 svg path添加文本_10 倍高清不花!大麦端选座 SVG 渲染
  3. use putty to log on OS
  4. dibian+9+oracle+java_debian – 安装了Oracle Java,但在安装软件时,我得到了`你的Java版本不受支持....
  5. mysql中count(*)和count(1)和count(column)区别
  6. 计算机工程师英语介绍,数据库系统工程师计算机专业英语(四).doc
  7. Sql Server REPLACE函数的使用
  8. 苹果高管:M1芯片iPad Air是为内容创作者和学生等设计
  9. 刷新mac地址命令_常用网络检测命令-必备技术
  10. 一文详解Python中的*可变参数与**关键字参数
  11. 彻底解决Android GPS没法定位这一顽固问题
  12. 不要被后端糊弄了,我来告诉你网关是什么
  13. [人工智能-综述-9]:科学计算、大数据分析、人工智能、机器学习、深度学习全面比较
  14. Discuz!风格模版初级不完全修改教程
  15. seo整站优化到底该从哪些方面进行着手(干货分享)
  16. matlab 冒号,MATLAB冒号符号
  17. 谷歌浏览器网页打不开怎么办
  18. session垃圾回收机制
  19. 短信验证码和发送短信按钮没有反应
  20. AI|优必选称准备IPO 此前公司机器人曾登上春晚舞台

热门文章

  1. [Linux] linux下安装配置 zookeeper/redis/solr/tomcat/IK分词器 详细实例.
  2. 【微信网页直接下载app】微信跳转-微信浏览器中直接唤起本地浏览器和App
  3. 后台开发常用mysql语句_v1.0
  4. Tensorflow模型加载与保存、Tensorboard简单使用
  5. 最长不下降子序列 (O(nlogn)算法)
  6. 《C++ Primer Plus(第六版)》(25)(第十三章 类继承 笔记)
  7. UI Automation 简介
  8. 2008R2Win7管理九DNS冗余和恢复
  9. [Leetcode][第108题][JAVA][将有序数组转换为二叉搜索树][二分法][递归]
  10. router锚点和html锚点,hash模式下Vue-router页面返回锚点(scroll behavior)实现