脚本之家
你与百万开发者在一起
作者 | 黄邦勇帅(原名:黄勇)
出品 | 脚本之家(ID:jb51net)
第1节 理解字符
一、字体的设计原理(字符集、编码、字体三者的关系)
1、字符集:就是各种字符的集合,比如Unicode就是一个字符集,它使用2个字节(即最多65535个)来表示所有的字符。
2、编码:一个字符要能被计算机所接受,需要进行两次编码,因为计算机只能表示二进制,对于人们常使用的10进制来讲不是很方便,因此字符的第一次编码就是把相应的字符使用一个整数值与其相对应,比如ASCII码字符集把字符'a'编码为10进制的61,就是一次编码,Unicode字符集也是一次编码。为了能让计算机的二进制识别,需要把第一次编码后的整数值再次编码为二进制值,比如使用一个字节来表示字符'a'一次编码后的整数值61,再如中文汉字使用两个字节进行表示,再如对于Unicode字符集有3种不同的二次编码方案,分别是UTF-8,UTF-16和UTF-32,目前使用较多的是使用UTF-8来存储的Unicode字符集。
3、字形(glyph):用于表示字符的外形,比如字母a的ASCII码为61,但这个字母可以以多种外形对其进行书写,再如中文字符中的每一笔画都是一个字形。注:glyph也翻译为图元,图像。其实对象的外形都是使用图元进行描述的。
4、字形与字符的关系:一个字形可以用于表示多个字符,一个字符也可以由多个字形组成,比如中文字符,就经常共享字形,而且是由多个字形组成的。字符的衬线、粗细等都是字形设计的元素。
5、字体:是一个拥有相同设计风格的字形及从字符到字形映射关系的集合,字体使字符能被显示出来,字体是计算机显示文字的一种方式,比如早期电报就把字符表示为一长串数字,这一长串数字就相当于是字符集,当接收到电报后,是使用宋体、草书或者其他形式显示出来,就需要使用字体了,每种字体都有一个相应的名字,比如“Times New Roman”、“宋体”等,相同的字体显示的字符具有相似的风格,比如以宋体显示的字体,其风格都是相似的。另外,字体名通常有版权,是受到法律保护的。
6、计算机显示字符的原理简述:当计算机接收到一串二进制之后,表示的具体是什么字符,需要由字符集来决定,然后字符需要被显示出来(即字体以什么外观进行显示),这时就需要寻找相应的字体,若字库中没有相应字符的字体,则可能会被显示为乱码,所以要让计算机正确显示文字,不仅编码应正确,还要有相应的字体才行。
二、Unicode对字符的理解1、用户感知字符(user-perceived character)与字形族 (grapheme cluster)
用户感知字符就是指的人们认为的一个字符,字形族是用于近似表示用户感知字符的。计算机中的一个“字符”通常就是一个Unicode代码值,但是,现实中人们认为的一个“字符”,可能并不是由一个Unicode代码值组成,而是由多个Unicode代码值组成的。比如
该字符会被人们认为是一个单个的字符,但实际上他是由两个Unicode代码值(即,两个字符)组成的,即由Unicode字符g (u+0067)和 (U+0308)组合而成。2、字符的成形 (shaping)
在Unicode中,把字符以最终形状显示出来的过程称为成形。渲染引擎不一定能正确的对用户感知的字符成形,比如,有些渲染引擎可能会把显示为两个字符,有可能不会显示。3、对字符的处理
由以上可知,用户感知字符与计算机理解的字符是不同的,而人们通常是以用户感知字符对字符进行理解的,因此,就有很多问题需要处理,比如,一个字符串中有多少个用户感知字符(即,怎样确定一个用户感知字符的边界),怎样对用户感知字符断行,怎样显示等,Unicode标准对这些方面都有详细的描述,有兴趣的读者可参阅Unicode有关这些方面的内容,本文重点介绍Unicode双向排序算法(bidi算法),bidi算法影响各个Unicode字符的排序顺序,至于字符能否最终成形为用户感知字符、怎样断行等内容不属于bidi算法的范围。
第2节 Unicode字符的分类与定向格式化字符
一、文本显示顺序与U nicode 双向算法 1、文本的方向 大多数语言的文本在水平方向都是按从左到右( Left To Right ,简称L TR) 的顺序显示字符的,但也有不少语言是按从右到左( Right To Left ,简称RTL ) 的顺序显示字符的(比如阿拉伯语、希伯来语)。当然还有按垂直方向书写的文本,比如中国古代的汉字就是从上到下从右到左书写的,本文只讨论水平方向书写的文本,垂直方向书写的文本不予讨论。 双向文本是指一个字符串中同时包含LTR和RTL的文文,既包含从左到右的文本又包含从右到左的文本。现实中,从右向左书写的语言通常会夹杂着从左向右的文本(比如外语、引用、数字、符号等),因此,像阿拉伯语、希伯来语这些语言通常都是双向文本。另外,当在从左向右的文本中插入从右向左的文本时也会产生双向文本的问题。 2、逻辑顺序与显示顺序 逻辑顺序在U nicode 标准内规定为文本在内存中表示的顺序,而显示顺序就是最终显示在我们面前所看到的文本的顺序,文本的逻辑顺序和显示顺序并不一定会一致,比如对于从右向左显示的文本,显示顺序应是从右向左的,而逻辑顺序则可能是从左向右的。逻辑顺序是属于计算机底层的问题,不属于本文讨论的范围,我们需要解决的是文本的显示顺序题。 3 、U nicode 双向算法( bidi) 与定向格式化字符 1 ) 、对于双向文本,若不明确的确定文本的显示顺序,在显示时就可能会出现歧义,为此,需要为双向文本的显示定义一种算法(或者一种规则),用于规范双向文本的显示顺序。 2) 、通常有一种隐式算法(或称为隐式双向排序或隐式布局算法)来定义双向文本的显示,但是隐式算法并不足以产生可供理解的文本,为此,对某些字符的显示顺序需要明确地进行控制,就是使用一系列的控制符(类似于HTML中的元素)来控制字符的显示顺序,这些控制符被U nicode 称为 定向格式化字符 。比如,使用R L O控制符来控制字符从右向左显示,PDF表示RLO的终止字符,那么a b cd RLO EF GH PDF x ,将被显示为a b cd HG FE x ,可见,U nicode 控制符的原理与HTML的元素是相似的。 3) 、定向格式化字符只影响文本的显示顺序,在其它方面会被忽略,也就是说定向格式化字符不会对文本的比较、断句、词法分析、数值分析等方面造成影响。 4) 、U nicode 双向算法(也称为b idi) 是对隐式算法的扩展, U nicode 双向算法定义了定向格式化字符(即控制符),并且定义了一套算法,用于规定这些控制符对需要显示的字符产生怎样的影响。 二、U nicode 对字符的分类及输入方法 1、字符的属性Unicode为字符定义了很多属性,以用于描述该字符,比如Bidi_Paired_Bracket_Type属性用于描述该字符是开括号(值为open)还是闭括号(值为close),再如General_Category描述了该字符的通用类别,比如若该字符是行分隔符,则值为Line_Separator,是控制符,则值为Control等。
2 、字符的类型 U nicode 为每个U nicode 字符都定义了一种类型(称为双向字符类型或b idi 类型),双向字符类型被分为:强字符(强类型)、弱字符(弱类型)、中性字符(中性类型)、定向格式化字符,表 1 为具体的分类方法(定向格式化字符的分类见后文)表1 字符的类型 | |||
分类 | 类型 | 简述 | 范围 |
强字符(strong) | L | left to right | LRM(见表2),大部分字母、音节、汉字、非欧洲或非阿拉伯数字 |
R | right to left | RLM(见表2),希伯来字母和相关的标点符号 | |
AL | right to left Arabic | ALM(见表2),阿拉伯语(Arabic)、它拿字母(Thaana)、叙利亚字母,及大多数特定于这些文字的标点符号 | |
弱字符(weak) | EN | 欧洲数字(European Number) | 欧洲数字、东阿拉伯-印度数字,经常使用的数字1,2,3等就是属于EN类型 |
ES | 欧洲数字分隔符 | 加号,减号 | |
ET | 欧洲数字终止符 | 度的符号,货币符号,比如,$(美元),¥(人民币)等 | |
AN | 阿拉伯数字 | 阿拉伯-印度数字,阿拉伯小数和千位分隔符,平时使用的数字虽然叫做阿拉伯数字,但阿拉伯拥有自已的数字,比如,4的阿拉伯数字字符为 ٤ (u+0664) | |
CS | 普通数字分隔符 | 冒号,逗号,句点(即小数点),不间断空格(no-break space)等,注意:单引号、双引号、分号不属于该类型,中文的句号也不属于该类型 | |
NSM | 无间距标记(Nonspacing mark) | 属性General_Category为以下值的字符:Mn(Nonspacing_Mark)和Me(Enclosing_Mark)比如,组合用发音字符的上左角 ̚ (u+031A),西非书面文中的ࣾ (u+08FE) 。详见后文对组合字符的讲解 | |
BN | 中性边界 | 不是明确给定类型的字符,比如:可忽略的默认值,非字符,控制字符等。比如,广义标点中的不可见乘号(u+2062)就是BN类型 | |
中性字符(neutral) | B | 段落分隔符 | 段落分隔符(u+2029),适当的换行符函数,高级别确定段落的协议 |
S | 节分隔符(Segment Separator) | Tab | |
WS | 空白(Whitespace) | 空格,图形空格,行分隔符,换页符,常用标点符号的空格等 | |
ON | 其他中性符 | 所有其他字符,包括对象替换字符,比如,[、]、(、)、"、'、@、&、*、、<、>、|、{、}、;(分号)、!、?、~、=。注意:/ 属于CS类型、%、#属于ET类型 | |
定向格式化字符 | 见后文 |
3、定向格式化字符的分类
字向格式化字符分为隐式定向格式化字符和显示定向格式化字符两大类,显示定向格式化字符又分为显示定向嵌入格式化字符、显示定向重写格式化字符、显示定向隔离格式化字符,分别简称为嵌入格式化字符、重写格式化字符、隔离格式化字符。其中隔离格式化字符是在Unicode 6.3中引入的。具体的分类规则详见表2
表2 定向格式化字符的分类 | ||||
类型 | 控制符 | Unicode代码 | 简述 | 说明 |
隐式定向格式化字符 | LRM | U+200E | left to right mark | 从左到右的零宽度字符 |
RLM | U+200F | right to left mark | 从右到左的零宽度非阿拉伯字符 | |
ALM | U+061C | arabic letter mark | 从右到左的零宽度阿拉伯语字符 | |
显示定向嵌入和重写格式化字符 | LRE | U+202A | left to right embedding | 嵌入。把后面的文本看作是从左到右(LRE)或从右到左(RLE)的嵌入 |
RLE | U+202B | right to left embedding | ||
LRO | U+202D | left to right override | 重写。强制改变其后的文本的方向为从左向右(LRO)或从右向左(RLO) | |
RLO | U+202E | right to left override | ||
U+202C | pop directional formatting | 嵌入和重写终止符。用于终止LRE、RLE、LRO、RLO的作用范围 | ||
显示定向隔离格式化字符 | LRI | U+2066 | left to right isolate | 从左到右(LRI)或从右到左(RLI)的隔离之后的文本 |
RLI | U+2067 | right to left isolate | ||
FSI | U+2068 | first strong isolate | 隔离之后的文本,文本的方向由第一个非嵌套在隔离中的强字符决定 | |
PDI | U+2069 | pop directional isolate | 隔离终止符。用于终止LRI、RLI、FSI的作用范围,该终止符还会同时终止LRE、RLE、LRO、RLO的作用范围 |
4、组合字符
1)、组合字符(Combining character)是指General_Category属性的值为Mc (Spacing Combining Mark,间距组合标记)、Mn (Nonspacing Mark,无间距标记)、Me(Enclosing Mark,嵌入标记)的所有字符。
2)、组合字符通常用于与它的基本字符组合为一个字符,比如
以上字符由字符g (u+0067)和 (U+0308)组合而成,其中字符g就是基本字符,而U+0308就是组合字符。
3)、无间距标记(Mn)通常不单独占据空间位置,其占据的位置取决于它的基本字符。
5、零宽度字符 ( LRM、RLM、ALM )
表2中的LRM、RLM、ALM是一种零宽度字符,可将其理解为在该处插入了一个相应方向的强字符,但该字符是不可见的(宽度为零所以不可见)。比如aLRMb,相当于在a和b之间插入了一个从左向右的强字符,但该字符宽度为零且不可见(即不会被显示)。
6、与定向格式化字符对应的HTML5元素和CSS等效项
1)、HTML5没有提供对LRE、RLE、LRO、RLO的精确等效项,可使用CSS来获取LRE、RLE、LRO、RLO、LRI、RLI、FSI的精确等效项。表3为HTML和CSS与bidi算法的等效情形。由表3可见,在HTML5中,bdi元素更多用于对字符的隔离,以避免文本被周围字符方向性所影响,或避免隔离的文本影响周围字符的方向性,而bdo元素主要用于强制改变文本的方向性。
2)、注意:HTML5与HTML4.0不同,早期版本的bdi元素与隔离(LRI、RLI)相对应,bdo元素与重写(LRO、RLO)相对应。
表3 各平台对bidi算法的实现 | ||
bidi算法 | HTML5的等效项 | CSS的等效项 |
RLI ... PDI | dir = "rtl" (任意元素的dir属性) | direction:rtl; unicode-bidi:isoloate; |
LRI ... PDI | dir = "ltr"(任意元素的dir属性) | direction:ltr; unicode-bidi:isoloate; |
FSI ... PDI | <bdi>或dir = "auto" | unicode-bidi:plaintext; |
RLE ... PDF | 无对应元素 | direction:rtl; unicode-bidi:embed; |
LRE ... PDF | 无对应元素 | direction:ltr; unicode-bidi:embed; |
RLO ... PDF | 无对应元素 | direction:rtl; unicode-bidi:bidi-override; |
LRO ... PDF | 无对应元素 | direction:ltr; unicode-bidi:bidi-override; |
FSI RLO ... PDF PDI | <bdo dir = "rtl"> | direction:rtl; unicode-bidi:isoloate-override; |
FSI LRO ... PDF PDI | <bdo dir = "ltr"> | direction:ltr; unicode-bidi:isoloate-override; |
7、以Unicode代码(即数字)的形式输入字符
1)、注意:定向格式化字符是Unicode字符集中的一个字符,只是不会被显示出来,每个定向格式化字符都有其对应的Unicode代码,比如RLO的Unicode代码为U+202E
2)、Unicode字符可在Word、浏览器、记事本等应用上以Unicode代码的形式输入,定向格式化字符同样可以被输入,但是不会被显示
3)、HTML的格式如下:
&#x+mmmm;(16进制),或 +mmmm;(10进制) 注意:末尾有个分号
示例:a表示字符a
4)、CSS的格式为:+mmmm(16进制)
示例:0061表示字符a
5) 、W ord 的方法如下(注:记事本采用的是W ord 的第2种方法):方法1:输入相应的Unicode代码,然后选中该代码或在该代码末尾按下ALT+x,这时会把相应代码转换为字符,同时在Word中还可使用ALT+x把相应的字符转换为相应的Unicode代码。
方法2:按住ALT键不放,然后以10进制的形式输入Unicode代码,然后释放ALT键,便会产生相应的字符,比如,按住ALT键,同时输入97,然后释放ALT时便会产生字符a。
方法3:选择【插入】---【符号】---【其他符号】,弹出一个对话框,在该对话框中插入Unicode字符,其界面和使用方法如下图所示
第3节 运行等级与隔离运行序列
一、基本名词 1、嵌入等级(或嵌入水平) ( ( level ,可翻译为:等级、水平、级别):表示字符的嵌入层次,数字越大嵌入得越深,需要注意的是,在b idi 算法中,字符串中的每个字符都有一个嵌入等级。 2 、基础方向 ( base direction) :分段的方向被称为基础方向,基础方向决定了该段文本从浏览器的左侧还是右侧开始书写。 3 、隔离启动器 :是对L RI 、RLI、FSI的统称,注意:隔离启动器不包括PDI 4 、嵌入启动器 :是对L RE 、RLE、LRO、RLO的统称,注意:嵌入启动器不包括PDF 二、运行等级和隔离运行序列 1、 运行等级( level run) :也称为 定向运行 ( directional run) ,是指具有相同嵌入等级的字符所形成的最大子串,该子串与其直接接触的前后字符的嵌入等级不相同,比如a b cd RLE ef gh PDF kk mm ,假设分段的嵌入等级为0,则字符a、 b 、 c 、 d( 含其中的空格)的嵌入等级都为0,字符e、 f 、 g 、 h 的嵌入等级都为1,字符k、 k 、 m 、 m 的嵌入等级为0,因此,该字符串共有3个运行等级,分别是子串a b cd ,子串e f gh ,子串k k mm 。 2、 隔离运行序列(简称为运行序列或序列):是由一系列运行等级组成的序列,其规则如下:1)、含有隔离启动器时:除最后一个运行等级外,隔离运行序列中运行等级的最后一个字符是隔离启动器,与该隔离启动器匹配的PDI是序列中下一个运行等级的第一个字符,也就是说,序列中的运行等级是以隔离启动器结束的(最后一个运行等级除外),以PDI开始的(除第一个运行等级外)。
2) 、无隔离启动器时:此时每个运行等级构成一个独立的隔离运行序列。 3 、隔离运行序列具有如下特点:1)、每个运行等级只属于一个隔离运行序列,也就是说,不存一个运行等级属于两个序列的情形。
2)、在同一个隔离运行序列中所有的运行等级具有相同的嵌入等级,因为隔离运行序列是以隔离启动器开始一个运行等级,又以与其匹配的PDI开始另一个运行等级,很明显,这两个运行等级具有相同的嵌入等级。
3)、紧随着隔离启动器之后的运行等级会开启一个新的隔离运行序列,与之匹配的PDI之前的运行等级会结束它的隔离运行序列。
4、隔离启动器的重要规则: 隔离启动器和与其匹配的PDI拥有的嵌入等级是提升之前的原始嵌入等级,而不是提升之后的嵌入等级。 5、示例- 以下示例的“文本”表示实际输入的内容,其中的符号“⋄”不属于文本的内容,该符号只是为了提高示例的清晰度,以方便阅读。
- 以下示例均假设分段的嵌入等级为0
示例1(含隔离启动器):
分析如下文本的运行等级和隔离运行序列
文本1 ⋄ RLI ⋄ 文本2 ⋄ PDI ⋄ 文本3 ⋄ RLI ⋄ 文本4 ⋄ PDI ⋄ RLI⋄ 文本5 ⋄ PDI ⋄ 文本6
1、运行等级的分析方法:
- |文本1 ⋄ RLI |构成一个动行等级,因为在RLI之后的 |文本2 | 与 |文本1 ⋄ RLI | 的嵌入等级不相同,|文本1 ⋄ RLI |的嵌入等级为0,而 |文本2 | 的嵌入等级为1,因此 |文本1 ⋄ RLI | 构成一个运行等级,其等级为0。此处应用了规则:RLI和与之匹配的PDI的嵌入等级是提升之前的嵌入等级。
- |文本2 | 构成一个运行等级,因为在 |文本2 | 之前的RLI和之后的PDI与 |文本2 | 的嵌入等级不相同,|文本2 | 之前的RLI和之后的PDI的嵌入等级都为0,而 |文本2 | 的嵌入等级为1,因此,|文本2 | 构成一个运行等级。
- 其余运行等级的分析方法与以上类似,不再重述。
|文本1 ⋄ RLI | 为隔离运行序列中的一个运行等级(以RLI结束),与该RLI匹配的PDI,即 |文本2 | 之后的PDI是该序列中的下一个运行等级的开始,因此 | PDI ⋄ 文本3 ⋄ RLI | 是该隔序运行序列中的下一个运行等级(以RLI结束),同理,|文本4 | 之后的 | PDI ⋄ RLI | 是该序列中的再下一个运行等级,| PDI ⋄ 文本6 | 是该序列中的最后一个运行等级,因此,第一个隔离运行序列中的运行等级包含 |文本1 ⋄ RLI | 、| PDI ⋄ 文本3 ⋄ RLI |、 | PDI ⋄ RLI | 、| PDI ⋄ 文本6 | 四个运行等级。
|文本2 | 独自构成一个隔离运行序列,因为,紧随着隔离启动器之后的运行等级会开启一个新的隔离运行序列,与之匹配的PDI之前的运行等级会结束它的隔离运行序列。|文本2 | 位于 |文本1 ⋄ RLI | 中的RLI之后,同时位于 | PDI ⋄ 文本3 ⋄ RLI | 中的PDI之前,因此 |文本2 | 独自构成一个隔离运行序列。
其余隔离运行序列的分析方法与以上相同,不再重述。
3、最终的运行等级和隔离运行序列如下所示:
l 运行等级(共有7个):
|文本1 ⋄ RLI | → 等级0
|文本2 | → 等级1
| PDI ⋄ 文本3 ⋄ RLI | → 等级0
|文本4 | → 等级1
| PDI ⋄ RLI | → 等级0
|文本5 | → 等级1
| PDI ⋄ 文本6 | → 等级0
l 隔离运行序列(共有4个)
|文本1 ⋄ RLI | | PDI ⋄ 文本3 ⋄ RLI | | PDI ⋄ RLI | | PDI ⋄ 文本6 | → 等级0
|文本2 | → 等级1
|文本4 | → 等级1
|文本5 | → 等级1
4、图形法表示运行等级和隔离运行序列
图 1 为运行等级和隔离运行序列的图形表示法,等级= 0 的隔离运行序列由4个运行等级组成(虚线上方对应的运行等级),小括号范围内的运行等级不属于该隔离运行序列,等级= 1 的隔离运行序列虽然是画在同一行上的,但表示的是3个等级= 1 的隔离运行序列而不是一个由多个运行等级组成的等级= 1 的隔离运行序列,等级= 0 的隔离运行序列才表示的是一个由多个运行等级组成的隔离运行序列。示例2(不含隔离启动器):
分析如下文本的运行等级和隔离运行序列,本示例主要是要明白以下分段1的文本与分段2和分段3的文本的隔离运行序列的区别
分段1:文本1 ⋄ RLE ⋄ 文本2 ⋄ PDF ⋄ RLE ⋄ 文本3 ⋄ PDF ⋄ 文本4
分段2:文本1 ⋄ RLE ⋄ 文本2 ⋄ PDF ⋄ 文本3 ⋄ RLE ⋄ 文本4 ⋄ PDF ⋄ 文本5
分段3:文本1 ⋄ RLI ⋄ 文本2 ⋄ PDI ⋄ RLI ⋄ 文本3 ⋄ PDI ⋄ 文本4
1、各分段的图形表示法分别如图2、图3、图4所示:
2、运行等级的分析方法:
注:根据bidi的算法,字符RLE、LRE、PDF会被移除,因此,分析时可忽略。但是,RLI、LRI、FSI、PDI不会被移除,因此,分析时不可忽略。
分段1:很明显文本1、文本4的嵌入等级为0,文本2和文本3的嵌入等级1,因此,分段1共有3个运行等级,如下所示
|文本1 | → 等级0
|文本2 | |文本3 | → 等级1
|文本4 | → 等级0
- 分段2:文本1、文本3、文本5的嵌入等级0,文本2和文本4的嵌入等级都为1,因此,分段2的运行等级如下所示
分段3:文本1 ⋄ RLI、PDI ⋄ RLI、PDI ⋄ 文本4的嵌入等级为0,文本2和文本4的嵌入等级为1,因此,分段3的运行等级如下所示
3、隔离运行序列的分析方法:
分段1:没有隔离启动器,因此每个运行等级构成一个隔离运行序列,因此,其隔离运行序列共有3个,如下:
分段2:与分段1相同,每个运行等级构成一个隔离运行序列,因此,分段2共有5个隔离运行序列,如下:
序列1: | 文本 1 | → 等级 0 序列 2 : | 文本2 | → 等级 1 序列 3 : | 文本 3 | → 等级 0 序列 4 : | 文本 4 | → 等级 1 序列 5 : || 文本 5 | → 等级 0 分段3:因含有隔离启动器(其分析方法见示例1 ),其隔离运行序列如下(共有3 个) 序列1: | 文本1 ⋄ RLI | | PDI ⋄ RLI | | PDI ⋄ 文本 4 | → 等级 0 序列 2 : | 文本2 | → 等级 1 序列 3 : | 文本 3| → 等级 1第4节 bidi算法总览
对Unicode算法的完整测试,建议在以下由Unicode推荐的网站进行https://www.unicode.org/cldr/utility/bidic.jsp?
一、b idi 算法基本规则及思想 1、再次提醒:在bidi算法中定向格式化字符是作为一个字符处理的,比如a LRE b,这里共有3个字符,分别是a、LRE、b。2、强字符的方向是确定的,要么为从左向右(称为L类型),要么为从右向左(称为R类型),其中AL类型视为R类型。3、最终类型(在不引起混淆的情况下,本文有时会将其简称为类型):bidi算法会把每个字符(包括定向格式化字符)都转换为L、R、EN、AN四种类型之一,因此,最终类型是指字符的L、R、EN、AN类型。4、bidi算法规定(这是强制规定):L类型字符的嵌入等级必须是偶数,R类型字符的嵌入等级必须是奇数,若不满足以上要求,需对字符的嵌入等级进行调整(调整规则见I1~I2算法)或作硬性规定,此规则,通常可以反过来理解(注:字符串含有EN、AN时就不能这样理解了),即,最终的嵌入等级若是奇数,则该字符是R类型,若是偶数,则该字符是L类型。同理,运行等级,分段的嵌入等级也需要满足此规定。5、bidi算法的一个基本思想是,首先确定各个字符的嵌入等级,然后,把所有字符都调整为L、R、EN、AN四种类型之一,并根据字符的这些类型调整字符的嵌入等级为偶数或奇数,然后对调整后具有相同嵌入等级组成的子串进行重排序并显示。也就是说,所有的字符类型,比如ON、LRI、WS、NSM等最后都会被调整为L、R、EN、AN四种类型之一。6、总体来讲,bidi算法分为4大步,即,初次确定各字符的嵌入等级(P、X系列算法)
调整字符为L、R、EN、AN四种类型之一(W、N系列算法),即,把字符的类型调整为最终类型
调整嵌入等级 ( I系列算法),调整后的嵌入等级为最终嵌入等级
重排序(L系列算法)。
- P系列算法用于解析分段的嵌入等级
- X系列算法用于解析分段中各个字符的嵌入等级,此步骤解析出的字符并不一定满足L类型的嵌入等级为偶数,R类型为奇数的规则,需在I系列进行调整。
- W系列算法用于解析弱字符的类型,即调整弱字符的类型为最终类型
- N系列算法用于中性和隔离格式化字符的类型为最终类型
- I系列算法用于调整各字符的嵌入等级为奇数或偶数。
- L系列算法用于重排序
RLE、LRE、RLI、LRI、FSI、LRO、RLO会提升之后的字符的嵌入等级,具体怎样提升的,详见下一小节对X系列算法的讲解。
PDF、PDI会把之后字符的嵌入等级还原到之前的嵌入等级。
算法X9规定,RLE、LRE、RLO、LRO、PDF、BN会被移除,移除后这些了符只会作为占位符,在之后的分析中,这些字符表现得就像不存在一样,但是FSI、LRI、RLI、PDI不会被移除,由于对嵌入等级和字符类型的调整都是在该步骤之后,所以RLE、LRE、RLO、LRO、PDF、BN这些字符对整个字符排序的影响可忽略,其嵌入等级可不考虑,当使用图形法分析时,这些字符的嵌入等级使用x代替
控制字符RLE和PDF不会被显示
基础方向假设为L,即分段的嵌入等级为0
第11和12的个字符是希伯来文(控制字符RLE和PDF分别算一个字符)
注意:因字符串中包含有从右向左的希伯来文,字处理软件(比如word)显示这些字符串的顺序可能与这些字符的顺序不一致,为避免显示上的混乱,上面的字符串是以图片形式给出的。
以下解题步骤中的Unicode代码为16进制,嵌入等级中的x表示该字符的嵌入等级不会被考虑,由算法调整后的选项以粗体加灰色背景显示。
本示例暂不讲解空格的处理方法,
3、步骤3和步骤4:解析弱字符、中性字符、隔离格式化字符
4、步骤5:调整各字符的嵌入等级
5、步骤6:反转各子串
反转等级2:
反转等级1,即,在上步基础上的第7~11(含空格,不含控制符)的个字符,该次反转是最终显示顺序,结果为:
参考文献:http://www.unicode.org/reports/tr9/
本文作者:黄邦勇帅(原名:黄勇)
声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。
● 程序员35岁,依然可以“横行职场”
● 脚本之家粉丝福利,请查看!
● 那些裸辞的程序员,都干嘛去了?
● 致敬经典:Linux/UNIX必读书单推荐给你
● 9月份Github上最热门的JavaScript开源项目