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

1.

首先要注意的是,代理Surrogate是专属于UTF-16编码方式的一种机制,UTF-8和UTF-32是不用代理的。

如前文所述,为了让UTF-16能继续编码基本平面后面的增补平面中的码点值,于是扩展了UTF-16编码方式。

具体的扩展方法就是为其增加了代理机制,用两个对应于基本平面码点(即BMP代理区中的码点)的16位码元来表示一个增补平面码点,这两个用来表示一个增补平面码点的特殊16位码元就被称为“代理对”。

如果要用简单的一句话来概括,就是——所有大于0xFFFF的码点值(即增补平面码点编号,范围为0x10000~0x10FFFF,十进制为65536~1114111;注意,0xFFFF是十六位二进制数的最大值的十六进制表示)要编码成UTF-16编码方式的话,就必须使用代理机制(也就是用代理对来表示)。

2.

在UTF-16编码方式中,被合起来称为“代理对”的这两个16位码元就其中的任一单个码元而言,其实就直接对应于基本平面BMP中的某一个码点(即BMP中每一个码点的值必然对应于一个16位码元的值,因为基本平面中的码点总数为2^16=65536个,而16位码元能表示的值也等于2^16=65536个)。

这样一来,就产生了冲突:某个UTF-16码元到底是用于表示基本平面字符的码元,还是用于表示增补平面字符的代理对中的代理码元?

因此,为避免冲突,这些被用作“代理”的任一码元所对应的码点在基本平面中均未定义字符,即均没有指定字符。且形成“代理对”的两个码元所对应的码点其编号必定是连续的。

“代理”的真实含义或许就在于此:用两个基本平面中未定义字符的连续码点合起来“代为署理”增补平面中的码点。

因此,基本平面中这些用作“代理”的码点区域就被称之为“代理区(Surrogate Zone)”,其码点编号范围为0xD800~0xDFFF(十进制55296~57343),共2048个码点。

3.

增补平面一共有16个平面(即第2平面~第17平面),码点编号范围为0x10000~0x10FFFF(十进制为65536~1114111,码点总数为1048576个)。用两个代理码元表示,第一个码元的取值范围为0xD800~0xDBFF(二进制为1101 1000 0000 0000 ~ 1101 1011 1111 1111,十进制为55296 ~ 56319),第二个码元的取值范围为0xDC00~0xDFFF(二进制为1101 1100 0000 0000 ~ 1101 1111 1111 1111,十进制为56320 ~ 57343)。

因此,增补平面的第一个码点的编号0x10000其UTF-16编码就是0xD800 0xDC00(即0x10000经UTF-16编码后的码元序列为0xD800 0xDC00),其余类推。展现为二进制形式后如下:

    =====代理码元1=====    =====代理码元2=====

    1101 10pp ppxx xxxx     1101 11xx xxxx xxxx

其中代理码元1中的110110、代理码元2中的110111是定数,p、x是变数。去掉定数后组合起来就是pppp xxxx xxxx xxxx xxxx,共20位(2^20=1048576),刚好能够表示目前16个增补平面中的全部码点(0x10000~0x10FFFF,共1048576个)。其中pppp共4位,表示16个增补平面之一的编号(2^4=16);紧接着的16位x表示某个增补平面内的某个码点(2^16=65536个码点,而65536个码点/平面*16个平面=1048576个码点)。

4.

按照上面的编码方式,代理对里面的两个代理码元分别称之为高16位代理码元(或称为lead surrogates引导代理、前导代理),和低16位代理码元(或称为trail surrogates尾随代理、后尾代理)。

由于引导代理和尾随代理的值分别在0xD800~0xDBFF(十进制为55296 ~ 56319)之间和0xDC00~0xDFFF(十进制为56320 ~ 57343)之间,所以首尾两个代理总共可以组合出(56319-55296+1)*(57343-56320+1)=1048576个代理对,也就是总共可以表示1048576个增补码点,而目前Unicode标准所确定的16个增补平面的码点总和也就是65536*16=1048576个。

显然,Unicode字符集作为开放式字符集,未来不断增补字符进来,以至于增补平面超过16个,则按目前的UTF-16编码算法是无法编码的。也正是因为如此,UTF-16编码方式的扩展性、适应性是不足的,未来全面被具备高扩展性、高适应性的UTF-8编码方式代替是必然的。

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

5.

从增补平面的码点值通过基本平面中的代理对编码为增补平面字符的码元序列的具体算法如下:

1) 增补平面中的码点值(0x10000~0x10FFFF,二进制为0001 0000 0000 0000 0000~1 0000 1111 1111 1111 1111,对应的码点名称为U+10000~U+10FFFF)减去0x10000(二进制为0001 0000 0000 0000 0000),可得到20位长的比特组(值的范围为0x00000~0xFFFFF,二进制为0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111);

2)将得到的20位长的比特组分拆为两部分:高位10比特和低位10比特;

3)20位长的比特组中的高位10比特(值的范围为0x000~0x3FF,二进制为00 0000 0000~11 1111 1111)加上0xD800(二进制为1101 1000 0000 0000),得到第一个代理码元即引导代理(值的范围是0xD800~0xDBFF,二进制为1101 1000 0000 0000 ~ 1101 1011 1111 1111);

4)20位长的比特组中的低位10比特(值范围也是0x000~0x3FF,二进制为00 0000 0000~11 1111 1111)加上0xDC00(二进制为1101 1100 0000 0000),得到第二个代理码元即尾随代理(值的范围是0xDC00~0xDFFF,二进制为1101 1100 0000 0000 ~ 1101 1111 1111 1111);

5)将引导代理与尾随代理按前后顺序组合在一起成为“代理对”,就得到了增补平面字符的码元序列。

例如,增补平面中码点值为10437(码点名称为U+10437)的字符(?):

1)0x10437减去0x10000,结果为0x00437,二进制为0000 0000 0100 0011 0111。

2)分拆成高10位值和低10位值两部分:0000000001(即0x0001)及0000110111(即0x0037)。

3)添加0xD800到高位值,以形成高位的引导代理:0xD800 + 0x0001 = 0xD801(二进制为1101 1000 0000 0001)。

4)添加0xDC00到低位值,以形成低位的尾随代理:0xDC00 + 0x0037 = 0xDC37(二进制为1101 1100 0011 0111)。

5)将高位的引导代理与低位的尾随代理按前后顺序组合在一起成为“代理对”,就得到了增补平面字符?(码点名称为U+10437)的码元序列:1101 1000 0000 0001 1101 1100 0011 0111。

6.

下表总结了该转换。不同的颜色表示码点值是如何被分布到UTF-16码元序列中的,而由UTF-16编码过程中加入的代理附加位则以不同的红色(亮红色与暗红色)显示:

7.

显然,增补平面中的码点值从0x10000到0x10FFFF,共计0xFFFFF + 0x1个,即1,048,576个,刚好也就是需要20位来表示(2^20=1,048,576)。如果用两个16位长的码元组成的序列来表示,意味着引导代理要容纳上述20位中的前10位,尾随代理要容纳上述20位中的后10位。

另外,还要能够根据每个16位码元来直接判断该码元到底是属于引导代理(标志位为前6位11 0110,还剩下10位,因此总个数为2^10=1024个),还是属于尾随代理(标志位为前6位11 0111,也剩下10位,因此总个数也是2^10=1024个)。

为避免冲突,因此需要在基本多语言平面BMP中保留未定义Unicode字符的1024+1024=2048个码点,就可以容纳引导代理与尾随代理所需要的编号空间(码点空间、代码空间),也就是16个增补平面所需要的编号空间,共计1024*1024=2^20=1048576个码点。这BMP中的2048个码点对于BMP总计65536个码点来说,仅占3.125%(2048/65536=0.03125)。

8.

在UTF-16编码方式中,引导代理的后面应该是一个尾随代理,而尾随代理的前面就应该是一个引导代理;不能出现一个引导代理的后面是一个非代理的普通UTF-16码元的情况,也不能出现一个引导代理的后面还是一个引导代理的情况。

UTF-16文本(字符串)的最后一个码元不能是引导代理,不允许出现一个尾随代理的前面是一个尾随代理的情况,也不允许出现一个尾随代理的前面是一个非代理的普通UTF-16码元的情况;UTF-16文本(字符串)的第一个码元不能是尾随代理。

而单独的一个代理码元(不管是引导代理还是尾随代理)是不合法的,代理必须以一个“引导代理+尾随代理”编码对(即代理对)的形式出现。

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

9.

UTF-16的这种“代理对”编码规则保证了文本处理程序能够正确地访问和处理包括了基本平面和增补平面在内的全部UTF-16码元序列,并消除了基本平面字符和增补平面字符之间发生冲突的可能性。

因为引导代理和尾随代理码元被各自规定在一个特定范围内取值,所以很简单的一个原则就是:凡是在代理编码范围内的码元就是“代理”增补平面SP字符的“代理码元”,否则就是“基本平面BMP字符的码元”。由于BMP中的字符码元和代理码元分别在各自独立的编码范围内进行编码,所以对于一个符合格式规范的UTF-16码元来讲,它必须满足以下条件:

  - 非代理码元(BMP字符码元)必须避开代理码元所占用的范围0xD800~0xDFFF(二进制为1101 1000 0000 0000 ~ 1101 1111 1111 1111,共2048个);

  - 引导代理必须是代理对中的第一个码元;

  - 尾随代理必须是代理对中的第二个码元。

在处理UTF-16文本时,为了确保文本数据的完整性,绝对不能把任意一个代理从代理对中拆出来,也不能在代理对中间插入另一个字符的码元或码元序列。

10.

在UTF-16编码方式里面,一个Unicode字符码点值由一个或两个16位码元编码。所以,如果想在一个UTF-16码元序列里面判断某个码元是属于哪个字符的话,就需要检查那个码元的值,然后根据码元的类型(是否具有代理标志位)决定是否还需要向前或向后检查一个相邻的码元的值(可以不必理会除了前后相邻的两个码元之外的其他码元)。

由于引导代理、尾随代理、BMP字符码元,三者互不重叠,搜索就很简单,这意味着UTF-16具有“自同步”(self-synchronizing)性:通过仅检查一个码元就可以判断当前字符的下一个字符的起始码元,每个字符码元的边界很明确;同时,还具有“非传递”性:单独的一个UTF-16码元出错涉及的只是一个字符,不会传递到文本的其他部分去,因此,即使文本中某些字符数据遭到破坏,其影响也只是局部性的。

UTF-8也有类似优点。但许多早期的编码方式就不是自同步的,比如大多数的多字节编码标准如GBK、Big5等,必须从头开始分析文本才能确定不同字符的码元的边界;也不具有非传递性,局部字符数据被破坏,很可能传递到整个文件,导致整个文件无法正确显示。

因此,UTF-8和UTF-16编码方式所具有的“自同步性”、“非传递性”等特点除了增强抗干扰能力外,也提供了随机访问的能力。

11.

由于在大多数的文本数据中,代理对(即增补平面字符码元序列)出现的概率是很小的,很多情况下处理的还是非代理对(即基本平面字符码元序列),导致许多软件处理代理对的部分往往得不到充分的测试。这导致了一些长期的bug与潜在安全漏洞,甚至有些广为流行、得到良好评价的优秀软件也是如此。

因此,虽然编程时同时考虑文本中可能出现的不同存储长度的字符(基本平面字符是单16位编码,即单码元编码;增补平面字符是双16位编码,即双码元编码)并相应做出不同的处理,会比单纯只考虑16位编码在性能上要逊色一些。但实际上,现有的遵循定长16位编码规范但不能处理代理对的程序只需做很小的一点修改就可以同时处理基本平面字符和增补平面字符的编码了。

另外,需要特别注意的是,虽然Unicode标准规定BMP代理区(U+D800~U+DFFF)的码点值不对应于任何字符,即未作定义,但在UCS-2中,U+D800~U+DFFF是被定义了的,也就是已经用于某些字符了。不过,只要前后两个16位码元不是恰好构成了代理对,许多程序还是能把这些不匹配Unicode标准的字符码元正确地辨识、转换成合规的码元。这种由历史原因造成的码元序列按现在的Unicode标准来看,应算作是编码错误。

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

 

(未完待续)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437430.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java环境_配置java环境变量

1.找到此电脑我的电脑右键属性。2.点击高级系统设置。3.点击环境变量。4.点击新建。5.创建变量名JAVA_HOME必须是大写,变量值找到你的jdk的根目录复制下来,然后粘贴进去,点击确定。6.再次点击新建7.创建变量名为path、变量值,找到…

idea+JRebel实现项目热部署

今天发现这个插件,简直神了,太爽了,每次改完代码重启都要等半天,回不去了 第一步:安装插件 第二步:在线生成GUID 网址:在线GUID地址 第三步:打开jrebel 如下所示面板,选…

【转】刨根究底字符编码之十五——UTF-32编码方式

1. UTF-32在UTF目前常用的三种编码方式(UTF-8、UTF-16、UTF-32)中,是最为简单的一种编码方式。UTF-32编码方式不使用任何编码算法将Unicode字符码点值(即编号字符集CCS中的字符编号)转换为码元序列,而是将每个Unicode字符码点值直接表示为一个32位的码元…

小米手机证书信任设置在哪里_小米手机闹钟在哪里?闹钟怎么设置?怎么找到闹钟?...

随着智能手机的大量普及,人们会发现手机里的功能越来越多,绝不像以前那样只能打打电话发发短信那么简单了。而这些功能当中很多我们常用的功能有的人却不会用、不会设置,甚至根本找不到在哪里?比如说手机闹钟这个功能,…

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

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

win10切换输入法快捷键_输入法失灵怎么办?该怎么恢复?

我们都尝试过在使用win10的时候,输入法无法切换,只能打英文不能输入汉字,对于要打字、打游戏来说都是非常麻烦的。所以,当输入法有问题的时候,应该要怎么样修复?主编:渣渣辉,是兄弟就…

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

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

数位dp模板 最高位最低位_无纸化办公入门指南(数位板篇)

居家办公的这段时间,你们有没有遇到无纸化办公的问题?作为一名编辑,在我社规定不能寄纸稿的情况下,看电子稿成了常态。如果是word版尚且可以批注,不影响工作效率。但如果是PDF呢?怎样保证改稿的效率&#x…

AQS

1 AQS抽象的队列同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch 首先AQS维护了一个volatile 修饰的state和一个FIFO的同步队列(多线程争用资源被…

屏幕录像专家6.0_迅捷屏幕录像工具和屏幕录像专家哪个更好用?

随着社会的发展,屏幕录像常常都需要使用,然而有些新手朋友就想知道,到底是电脑中自带的屏幕录像专家好用,还是迅捷屏幕录像工具好用,这两者都是当前使用率比较高的软件,今天小编就为大家分析分析&#xff0…

【转】刨根究底字符编码【2.0版】(2):关键术语解释

上一篇中讲道,字符编码所涉及的面非常广,向下的话,涉及到计算机的底层技术,甚至是硬件实现。 因此,这里就让我们从字符编码涉及到的最基本的术语讲起。大部分术语你可能都已经有了解了,但我们现在从字符编…

Cause: java.sql.SQLSyntaxErrorException: Table ‘Chun.user‘ doesn‘t exist Springboot+MybatisPlus报错

排错:yml配置没问题,可以连接到数据库,导包没问题,数据库里面也有这个库和表, 最终发现是因为表名大小写的问题,因为我直接连接的阿里云服务器上的mysql,而mysql在linux下表名是区分大小写的导…

浏览器字体大小设置_CSS之 浏览器解析样式的过程

阅读本文约需要10分钟大家好,我是你们的导师,经常看我朋友圈的同学应该知道,我每天会在微信上给大家免费提供以下服务!1、长期为你提供最优质的学习资源!2、给你解决技术问题!3、每天在朋友圈里分享优质的技…

angular实现国密算法sm2、sm3和sm4的ts版,基于sm-crypto库实现,前后端实现

ts版,js的话直接根据npm文档调用就可以了! ts提供的方法有问题,所以还换了个思路来实现!而且因为不是nodeJs环境所以const sm4 require(sm-crypto).sm4这个在ts里是报错的导致无法实现,如果是使用的是electron那么使…

【转】.net异步性能测试(包括ASP.NET MVC WebAPI异步方法)

很久没有写博客了,今年做的产品公司这两天刚刚开了发布会,稍微清闲下来,想想我们做的产品还有没有性能优化空间,于是想到了.Net的异步可以优化性能,但到底能够提升多大的比例呢?恰好有一个朋友正在做各种语…

win7关机快捷键_电脑快捷键大全(上)

Windows快捷键1单独按Windows:显示或隐藏“开始”功能表WindowsBREAK:显示“系统属性“对话框WindowsD显示桌面或回复桌面Windows M最小化所有窗口WindowsShiftM:还原最小化的窗口CrtlShiftN:新建文件夹WindowsE:打开“我的电脑”…

【转】ASP.NET Web API 使用Swagger生成在线帮助测试文档,支持多个GET

以下为教程: 在现有webapi项目中,nuget安装以下两个插件 swagger.net.ui swashbuckle 安装完毕后可以卸载Swagger.NET,此处不需要! 安装完毕后屏蔽以下代码 直接运行调试 在浏览器的目录后面加上/swagger即可跳转到swagger调试页 此时如果没有注释. 项目属性里添加xml注释…

idea提示“ cannot access xxxxxxxx.class“的解决方法,idea的bug

同一个包下的public类使用报错,应该是idea的bug: file -> Invalidate Caches / Restart

电脑屏保在哪里设置_手机屏保调成绿色能护眼?真的吗?

说到护眼,你首先想到的是什么颜色?估计90%的人都会不自觉的想到绿色!因为从小家长和老师都会跟我们说:眼睛累了就多看窗户外的绿色植物。久而久之,一说到护眼,大家脑海里第一个想到的就是绿色。你会看到不少…

电脑休眠和睡眠的区别_关机、睡眠、休眠的区别

都知道电脑有关机、睡眠、休眠三种休息模式,但是后两个选项使用的人非常少,大多数人每次用完电脑都会选择立即「关机」。有人是为了让电脑「休息」,有人是为了低碳精神,还有人是因为下班不关电脑要罚款,不管是什么原因…