北大中文论坛 www.pkucn.com

标题: 请教关于“Surrogate” [打印本页]

作者: 红衣主教    时间: 2010-12-9 12:43
标题: 请教关于“Surrogate”
 
哪位帮忙简要的解释一下“Surrogate”这个概念,我看到论坛上有些发言中使用这个术语似乎是当作 CJK Ext.B 的同义词,但不知道具体指什么,韦剑版主给出了一个 Unicode 技术资料的连接但从头阅读太困难了,而且多是具体技术细节,在缺少基本概念轮廓的情况下一头雾水。

我想哪位能够概要的介绍一下基本概念,为什么要引入“Surrogate”这个概念,它对应什么编码系统,它起什么作用,在编码中如何发挥作用,与 CJK Ext.B 是什么关系,等等。

谢谢!
 
作者: 韦剑    时间: 2010-12-9 17:41
直接用其本意来理解就很好了,Surrogate就是“代理”的意思。那什么是代理呢?首先要注意的是,代理是专属于UTF-16编码格式的一种机制,UTF-8和UTF-32是不用代理的。我们知道,Unicode到目前为止总共确定了17个编码平面(1个基本平面和16个增补平面),合1114112个码位,那么很容易就可以算出,简单地用一个16位编码肯定无法表示那么多码位(因为2^16=65536)。为了让UTF-16能继续编码基本平面后门的增补平面,人们扩展了UTF-16编码格式,具体的做法就是为其增加了代理机制,用两个16位编码表示一个增补平面码位,这两个用来表示一个增补平面码位的特殊16位编码就被称为“代理对”——意思是它们是增补平面码位在16位编码中的“代理”,因为没办法直接用一个16位编码来表示一个增补平面码位。所以,“代理机制是CJK ExtB的同义词”这种看法是错误的,至少是不完整的。因为代理机制帮助UTF-16编码格式作用于所有16个增补平面,而CJK ExtB只是增补平面中的一块片区。

如果要用简单的一句话来概括的话,就是——所有大于U+FFFF(即U+10000 - U+10FFFF)的编码点要编码成UTF-16编码格式的话,就必须使用代理机制(也就是用代理对来表示)。
作者: yangming    时间: 2010-12-9 20:05
韋劍版主的答案令人豁然開朗,一直以來分不清UTF-8和UTF-16的區別,現在有點理解了,UTF-8由於採用八位二進制的字節為單位,避免了UTF-16只能採用Surrogate代理的尷尬,也就是——所有小於U+FFFF的編碼以三個字節表示,所有大於U+FFFF的編碼以四個字節表示。
作者: 红衣主教    时间: 2010-12-9 20:52
 
感谢韦香主高屋建瓴、言简意赅的解释,感觉有了一点门路,有了理解的方向。遇到具体问题再来请教。
 

[ 本帖最后由 红衣主教 于 2010-12-9 20:54 编辑 ]
作者: slt    时间: 2010-12-10 01:34
UTF-8 每字至少 1 byte,至多 4 bytes。1 byte 字符與 US-ASCII 相符。U+0080-07ff 是 2 bytes,0800-ffff 是 3 bytes,之後的是 4 bytes

UTF-32 或稱 UCS-4 ,每字都是 4 bytes

UCS-2 每字都是 2 bytes,但容納不了擴展平面的字符,於是,Unicode 撥出了 d800-dfff 的位置,以兩個這些編碼點來代表擴展平面的字符。這就叫 surrogate. 加上了這種變通方法後,UCS-2 就改名成了 UTF-16。擴展平面的字符,所佔的位置就是 2 x 2 bytes

洋紅色字是後改的

[ 本帖最后由 slt 于 2010-12-11 01:26 编辑 ]
作者: 红衣主教    时间: 2010-12-10 02:24
感谢楼上进一步补充。
原来看 Unicode Roadmap 时就注意到 U+D800 - DFFF 的“空”位(http://www.unicode.org/roadmaps/bmp/),注释文字写的是「High-half zone of UTF-16」,因为行文的差别,当时没有把它与「Surrogate」概念联系起来。你这样具体一说就清楚了。
另外上文中说「以兩個這些字符來代表擴展平面的字符」,意思是两个字符还是两个字节,如果是前者,为什么要用两个字符才代表一个扩展平面。

同时欢迎其他朋友进一步发表意见、解说或评论,谢谢大家!
 
 
作者: 韦剑    时间: 2010-12-10 08:34
5楼的意思是用两个基本平面的编码点来在UTF-16里面表示一个增补平面码位。当然他具体的表述容易让人误会。因为这里不能用“字符”概念,而应用“编码点”,所谓编码点,就是指形如U+XXXX这样的数字。因为在UTF-16的代理机制中,那两个特殊的16位编码的取值范围是“占用”了基本平面上的U+D800-U+DFFF这一区段(这一区段在Unicode编码分布表上的专属名字叫“(基本平面)代理区”),所以我想“代理”的直接含义就应该在于此吧——通过基本平面上一个特定编码点区间,代理表示增补平面上的编码点。
作者: 谢振斌    时间: 2010-12-10 11:37
扩展平面一共是16个平面,码点从U+10000开始到U+10FFFF。
用两个WORD表示,第一个WORD取值D800-DBFF,第二个WORD取值DC00-DFFF。
扩展平面的第一个码点 U+10000 就对应为 D800 DC00,其余类推。
展现为二进制形式看看:
==== WORD1 ====       ==== WORD2 ====   
1101 10pp ppxx xxxx      1101 11xx xxxx xxxx
其中01是定数,px是变数。
也即是说去掉定数后的 ppppxxxxxxxxxxxxxxxx 就是表达扩展平面的全部变数,其中pppp表示16个平面,后面的16个x表示平面内的位置。
作者: 红衣主教    时间: 2010-12-10 18:58
心怀感佩,继续获益中。

上面各位的发言和讲解,相辅相成,互为印证,读过深有启发。韦剑版主语言表述的精确有如箭不虚发,楼上谢振斌兄给出的数学描述,其严谨的结构甚至蕴涵一种美感。
体会上面编码的设计,可能还包含另外一重好处,这样可以使编码的高字和低字内在区分,文本编码作为一个数据流即使中间偶然出现个别错误,可以将错误限制在局部范围,不至于造成高字和低字可能发生的混淆继续蔓延殃及下文,有一定程度的容错能力,不知这样理解是否恰当。
 
 

[ 本帖最后由 红衣主教 于 2010-12-10 19:59 编辑 ]
作者: 谢振斌    时间: 2010-12-10 23:57
是的,UTF-8和UTF-16都可以做到任何一个字错码不至于影响到下一个字。每个字的边界是明确的。
这个特点除了增强抗干扰能力外,也提供了随机访问的能力。
作者: slt    时间: 2010-12-11 01:20
UTF-16 如果是 BMP 字符,不能保證錯碼不影響下一個字。

至於 UTF-16 和 UTF-32 編碼方面,
有分 BE 和 LE (Big Endian 大尾序, Little Endian 小尾序)。
平常見的是 Little-endian,例如 U+5173,
在“UTF-16 LE”的儲存方法,先是 73,後才是 51。
如果把“UTF-16 LE”誤當作“UTF-16 BE”,
那就會被誤判成 U+7351 了。

另要謝謝韦剑板主,指出小弟的錯誤和會混淆之處

[ 本帖最后由 slt 于 2010-12-11 01:28 编辑 ]
作者: 谢振斌    时间: 2010-12-11 09:44
UTF-16 如果是 BMP 字符,除非是吃掉一些比特或多出一些比特,否则也是不会影响下一个字的。
因为对BMP来说,每16比特定长为界,自然不会乱。

至于LE和BE,只是影响每个字的解读方法而已,和前述的乱码问题无关。
作者: 韦剑    时间: 2010-12-11 13:13
原帖由 红衣主教 于 2010-12-10 18:58 发表
体会上面编码的设计,可能还包含另外一重好处,这样可以使编码的高字和低字内在区分,文本编码作为一个数据流即使中间偶然出现个别错误,可以将错误限制在局部范围,不至于造成高字和低字可能发生的混淆继续蔓延殃及下文,有一定程度的容错能力,不知这样理解是否恰当。


是的,在Unicode标准的第三、第五章有明文规定的UTF-16代理对处理规则,每一个“希望”自己符合Unicode标准的程序都应该遵守这样的规则。

    UTF-16编码格式中代理对的处理



  代理对里面的两个代理分别称之为高16位代理编码单元(或引导代理),和低16位代理编码单元(或尾随代理)。由于引导代理和尾随代理的编码空间分别在U+D800到U+DBFF之间和U+DC00到U+DFFF之间,所以首尾两个代理总共可以组合出(DBFF-D800+1)*(DFFF-DC00+1)=1048576个代理对,也就是总共可以表示1048576个增补编码点。另一方面,目前Unicode标准所确定的16个增补平面的编码点容量的总和也是65536*16=1048576个。

  在UTF-16编码格式里面:引导代理的后面应该是一个尾随代理,而尾随代理的前面就应该是一个引导代理;不能出现一个引导代理的后面是一个非代理的普通UTF-16编码单元的情况,也不能出现一个引导代理的后面还是一个引导代理的情况,UTF-16文本(字符串)的最后一个编码单元不能是引导代理;不允许出现一个尾随代理的前面是一个尾随代理的情况,也不允许出现一个尾随代理的前面是一个非代理的普通UTF-16编码单元的情况,UTF-16文本(字符串)的第一个编码单元不能是尾随代理;单独的一个代理(不管是引导还是尾随代理)编码单元是不合法的,代理必须以一个“引导代理+尾随代理”编码对的形式出现。UTF-16的这种“代理对”编码规则保证了文本处理程序能正确地访问和处理包括了BMP和增补字符的UTF-16 编码单元序列,并将两者之间发生冲突的可能性减到了最小。

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

  · 非代理编码单元必须是一个取值在0到D7FF或E000到FFFF之间的编码点;

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

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

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

  在处理UTF-16编码格式文本时,为了确保文本数据的完整性,绝对不能把任意一个代理从代理对中拆出来,也不能在代理对中间插入另一个字符。此外即使文本中某些字符数据遭到破坏,其影响也只是局部性的,这一点和大多数的多字节编码标准如GBK、Big5 等不同。单独的一个UTF-16编码单元出错涉及的只是一个字符。由于UTF-16编码格式的“非迭代”特性,像这种类型的错误是不会传递到文本的其他部分去的。

  因为在大多数的文本数据中,代理对(增补字符)出现的概率是很小的,很多情况下我们处理的还是非代理编码单元(BMP 字符)。虽然如果编程时要考虑到文本中可能出现的不同存储长度的字符(BMP 字符是单16位编码,增补字符是双16位编码)并相应做出不同的处理的话,会比单纯只考虑16位编码的处理程序在性能上要逊色一些,不过应该看到的是,现有的遵循定长16位编码规范但不能处理代理对的程序只需做很小的一点修改就可以同时处理BMP 字符和增补字符了。抛开性能这个硬性指标不谈(因为处理程序要考虑的情况越复杂,处理所需的时间就越长,这不是单纯通过技巧可以绕开的客观规律),单从应用需求的角度考虑,软件设计时同时把代理对和非代理编码单元的处理考虑进去还是有必要的。


也就是说,对于给定的一个UTF-16编码格式的文本字符串,随便访问上面的某一个16位编码单元,我最多只需要再向前或向后检查邻近的一个编码单元,就可以知道当前访问的那个编码单元所表示的是哪个Unicode编码点或者是不是错误的编码单元(无效编码单元)。这样,错误是很容易检查和排除的,不会发生那种一个错码就影响后面所有编码单元的事情(这样的事情在GB和Big5等ANSI编码中是很常见的)。

[ 本帖最后由 韦剑 于 2010-12-11 13:25 编辑 ]

[ 本帖最后由 韦剑 于 2010-12-11 14:09 编辑 ]
作者: slt    时间: 2010-12-12 15:06
當你整篇都是中文字時,採用UTF-16就有可能出現錯了會影响下一个字的事了。
作者: 谢振斌    时间: 2010-12-12 17:15
原帖由 slt 于 2010-12-12 15:06 发表
當你整篇都是中文字時,採用UTF-16就有可能出現錯了會影响下一个字的事了。

举个例子看看。
作者: slt    时间: 2010-12-13 00:32
原帖由 谢振斌 于 2010-12-12 17:15 发表

举个例子看看。


“举个例子看看” 是 “3E 4E 2A 4E 8B 4F 50 5B 0B 77 0B 77 ”,假設在 2A 和 4E 之間,多了一個 00,就變成 “举*譎偏୛୷”。
作者: 谢振斌    时间: 2010-12-13 10:39
原帖由 slt 于 2010-12-13 00:32 发表


“举个例子看看” 是 “3E 4E 2A 4E 8B 4F 50 5B 0B 77 0B 77 ”,假設在 2A 和 4E 之間,多了一個 00,就變成 “举*譎偏୛୷”。


你举的例子就是我前面说的,吃掉一些比特或多出一些比特,也就是让字边界错乱。实际上这种可能性极低,因为多数设备的储存或传输都有自身的边界约束,如磁记录、光刻记录等都有自己的调制同步机制,可以很好地避免这一点。
比较可能的是不可靠的位流或字节流串行通讯,确实可能存在丢失甚至多出一些内容。
类似RS232通讯是以字节(或更少位数)为单位进行传输的,是有可能发生,不过可靠性要求高的,会有多重校验措施来减少出错机会。
作者: 红衣主教    时间: 2010-12-13 21:49
我觉得上面两位讨论的见解并无实质性冲突,只是在各自默认的前提下看这个问题,才有一些表叙差异。

还有另一个稍有关联的问题请教谢兄,关于 Windows 多语言文本的后援字体(Fallback Font)问题,在注册表
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Language Pack\SurrogateFallback
分支下可以指定各 Surrogate 编码平面使用的后援字体,以替代显示当前所用任何字体中没有的字符,但是不知 BMP 平面的 Fallback Font 如何指定。我参阅的两个资料也语焉不详,或者是不是基本平面中只通过字体链接(Font Linking)来实现多语言文本的字体“通用”(逐个字体指定链接),没有 Fallback(统一指定任何字体的后援)机制呢。

附,我看到的资料:
http://msdn.microsoft.com/en-us/goglobal/bb688134.aspx
http://blogs.msdn.com/b/michkap/archive/2005/10/01/476022.aspx
 

[ 本帖最后由 红衣主教 于 2010-12-13 21:51 编辑 ]
作者: 谢振斌    时间: 2010-12-13 22:46
原帖由 红衣主教 于 2010-12-13 21:49 发表
我觉得上面两位讨论的见解并无实质性冲突,只是在各自默认的前提下看这个问题,才有一些表叙差异。

还有另一个稍有关联的问题请教谢兄,关于 Windows 多语言文本的后援字体(Fallback Font)问题,在注册表 分支下 ...


是的,我认同你以及slt所说的。

关于字体链接的问题,我所知的也和你差不多。似乎只能从FontLink里面逐个字体去设置,我也希望有更多的资料参考。
试试在SurrogateFallback 那里增加一个Plane0看看如何? 估计是无效的。
作者: 红衣主教    时间: 2010-12-14 03:10
细看,看到有这样一句话,
Font fallback is internal to Uniscribe, and applications cannot add new fallback fonts or modify existing ones.
(http://msdn.microsoft.com/en-us/goglobal/bb688134)


结合另一篇的叙述,应该可以理解为,BMP 的 Fallback 由系统掌握用户无法干预,增补平面则通过注册表指定。




欢迎光临 北大中文论坛 www.pkucn.com (http://www.pkucn.com/) Powered by Discuz! X2.5