Unicode双向算法(一)


Unicode®标准附录#9

UNICODE双向算法####

版本 Unicode 8.0.0
编者 Mark Davis, Aharon Lanin, and Andrew Glass
日期 2015-05-29
当前版本 http://www.unicode.org/reports/tr9/tr9-33.html
以前版本 http://www.unicode.org/reports/tr9/tr9-31.html
最新版本 http://www.unicode.org/reports/tr9/
提出的更新 http://www.unicode.org/reports/tr9/proposed.html
修正 33

摘要####

本附件是一份关于字符定位的规范,主要描述文本中字符的书写方向为从右到左的情况,例如阿拉伯语或希伯来语。

现状####

本文档已被Unicode成员和其他相关方面审阅,并由Unicode协会批准发布。这是一个稳定的文档,可以用作参考材料,或作为标准被其它规范引用。
  Unicode标准附录(UAX)虽然构成了Unicode标准的一部分,但UAX是作为一个单独的文档在网上发布。要是该版本的Unicode标准的一致性章节有相关规定,那么Unicode标准需要与UAX中的规范性内容保持一致,UAX的版本号对应着由它所构成的Unicode标准的版本。
  请以网上申报的形式提交勘误和其他意见 [Feedback]。有助理解本附件的相关资料可以在Unicode Standard Annex #41(Unicode标准附件 #41)中找到[Common References for Unicode Standard Annexes]。 有关Unicode标准的最新版本,请参阅[Unicode]。有关目前Unicode技术报告的清单,请参阅[Reports]。更多有关Unicode标准的版本信息,请参阅[Versions]。有关本附件的勘误表,请参阅[Errata]。

目录####

├ 1 引言
├ 2 定向格式化字符
│├ 2.1 显式定向嵌入
│├ 2.2 显式定向重写
│├ 2.3 终止显式定向嵌入和重写
│├ 2.4 显式定向隔离
│├ 2.5 终止显式定向隔离
│├ 2.6 隐式定向标记
│└ 2.7 标记和格式化字符
├ 3 基础显示算法
│├ 3.1 定义
││├ 3.1.1 基础BD1BD2BD3BD4BD5BD6BD7
││├ 3.1.2 匹配显式定向格式化字符BD8BD9BD10
│││    BD11BD12BD13
││├ 3.1.3 成对括号BD14BD15BD16
││└ 3.1.4 其他缩写
│├ 3.2 双向字符类型
│├ 3.3 解决嵌入等级
││├ 3.3.1 分段等级P1P2P3
││├ 3.3.2 显式等级和方向X1X2X3X4X5X5A
│││    X5bX5cX6X6AX7X8
││├ 3.3.3 隐式处理的准备X9X10
││├ 3.3.4 解决弱类型W1W2W3W4W5W6W7
││├ 3.3.5 解决中性类型和隔离格式化类型N0N1N2
││└ 3.3.6 解决隐式层次I1I2
│├ 3.4 解决重排序层次L1L2L3L4
│└ 3.5 成型定型?翻译
├ 4 双向一致性
│├ 4.1 中性边界
│├ 4.2 显式格式化字符
│├ 4.3 高层协议HL1HL2HL3HL4HL5HL6高等级协议
│└ 4.4 双向一致性测试
├ 5 实现说明
│├ 5.1 参考代码
│└ 5.2 保留的BN和显式格式化字符保留还是确定
├ 6 用法
│├ 6.1 结合
│├ 6.2 竖排文本
│├ 6.3 格式化
│├ 6.4 分离标点符号
│└ 6.5 转换为纯文本
├ 7 镜像
迁移问题
│└部分重组
致谢
参考
修改


1 引言####

Unicode标准规定内存表示顺序(memory representation order)称作逻辑顺序(logical order)。当文本在水平线上呈现时,大多数语言从左到右显示字符(from left to right)。然而,存在一部分的语言(例如阿拉伯语或希伯来语),它们的文本在水平方向上的自然排列是从右到左(from right to left)的。假如所有文本有一个统一的方向,文本的显示顺序将十分清晰。
  然而,当这些right-to-left语言使用书写方向为从左到右的数字时,实际上文本是双向的(bidirectional):即right-to-left 和left-to-right的混合文本。不单单是数字,插入英语单词或插入书写方向从左到右的其它语言也会产生双向文本。所以,假如没有明确的规范,当文本在水平方向上不统一时,在需要确定字符的显示顺序时就会出现歧义。
  本附录描述了用于确定双向Unicode文本的方向性的算法。该算法扩展了一些现有实现所采用的隐式模型,并增加显式格式化字符以应对特殊情况。当然,大多数情况下,并不需要添加额外的信息来获得正确的显示顺序。
  然而,在双向文本里,隐式双向排序并不足以产生可供理解的文本。为了处理这种情况,定义了一个最小的定向格式化字符集,用于渲染时控制字符的顺序。这允许准确地控制存在明显交换的显示顺序,并确保那些用于简单的项目的纯文本,如文件名或标签可以正确排列以供显示。
  定向格式化字符只用于影响文本的显示顺序。在其它方面,它们应该被忽略,即它们对文本的比较、断字、词法分析、数值分析方面均不构成影响。
  每个字符属于一种隐式双向类型(bidirectional type)。双向类型left-to-right和right-to-left被称作强类型,这种类型的字符叫做强方向性字符(简称强字符)。双向类型中与数字有关的被称为弱类型,这种类型的字符叫做弱方向性字符(简称弱字符)。除开定向格式化字符,剩下的双向类型和字符是中性的。本算法利用文本中的字符的隐式双向类型,来实现文本合理的显示顺序。
  处理双向文本时,仍然在逻辑顺序中解释字符,只是在显示时受到影响。双向文本的显示顺序取决于文本中字符的方向属性。注意:双向文本中存在重要的安全问题,详情请参阅[UTR36]。


2 定向格式化字符

三种类型的显式定向格式化字符被用于修改标准隐式Unicode双向算法(UBA)。此外,还有隐式定向格式化字符:right-to-left mark(RLM)和left-to-right mark(LRM)。以上这些格式化字符的影响仅限于当前分段,因此,它们被分段分隔符终止。
  这些格式化字符具有属性Bidi_Control,下表将格式化字符分成三组:

隐式定向格式化字符(Implicit Directional Formatting Characters) LRM, RLM, ALM
显式定向嵌入格式化字符和显式定向重写格式化字符(Explicit Directional Embedding and Override Formatting Characters) LRE, RLE, LRO, RLO, PDF
显式定向隔离格式化字符(Explicit Directional Isolate Formatting Characters) LRI, RLI, FSI, PDI

在网页上,所有类型的显式定向格式化字符(「嵌入」,「重写」和「隔离」)应该使用HTML元素bdibdo或HTML属性dir替代。但这并不适用于隐式定向格式化字符。如果需要了解更多信息,请参阅[UTR20]。
  当「嵌入」作用于一些显式格式化字符时,嵌入格式化字符内的文本范围并不与周围的文本相互独立。即在「嵌入」范围内的字符可以影响到外部的字符排序,反之亦然。然而,隔离格式化字符的不同于此。在「隔离」范围内的字符不能影响到外部字符的排序,反之亦然。「隔离」内的字符作为一个整体,对周围的字符影响效果与中性字符相同,而「嵌入」和「重写」对周围字符的影响与强字符相似。
  在Unicode 6.3中引入了定向隔离字符后,显而易见,使用定向嵌入会对其周围的字符产生很强的影响从而导致新问题。之所以选择引入新字符而不是改变旧字符的行为,是因为改变旧字符可能会对那些依赖于字符原有行为的旧文档产生不良影响。当然,一旦目标平台支持它们,在新文档上更鼓励使用定向隔离而不是嵌入 。

2.1 显式定向嵌入

以下字符代表着一段文本按嵌入处理。例如,在阿拉伯语句子中的一个英语引文,英语引文被标记为「嵌入的left-to-right」文本。如果在英语引号中有一个希伯来短语,那这个短语就被标记为「嵌入的right-to-left」的文本。在一个「嵌入」中可以嵌套另一个「嵌入」中,还可以嵌套在「隔离」和「重写」中。

| | || |
|:----|:----|
| 缩写 | 代码点 * | 名称 | 简述 |
| LRE | U+202A | LEFT-TO-RIGHT EMBEDDING | 将后面的文本看作left-to-right的嵌入|
| RLE | U+202B | RIGHT-TO-LEFT EMBEDDING | 将后面的文本看作right-to-left的嵌入 |
*:代码点(CodePoint)代表一个完整的Unicode字符。
  例如,要实现从右到左的行方向影响,可以在文本中嵌入RLE...PDF。(PDF格式将在「2.3节 终止显式定向嵌入和重写中介绍)」。

2.2 显式定向重写#####

在需要应对特殊情况下例如零件号码时,以下字符允许双向字符类型能被重写。但因为存在着安全问题,它们应该尽可能避免使用。有关更多信息,参阅[UTR36]。「重写」可以嵌套在另一个「重写」中,也可以嵌套在「嵌入」和「隔离」中。

| | || |
|:----|:----|
| 缩写 | 代码点 | 名称 | 简述 |
| LRO | U+202D | LEFT-TO-RIGHT OVERRIDE | 将后面的字符看作left-to-right的强字符 |
| RLO | U+202E | RIGHT-TO-LEFT OVERRIDE | 将后面的字符看作right-to-left的强字符 |
  这些字符的确切含义将在对算法的讨论中解析清楚。以「right-to-left重写」为例,它可以用于使得由英文、数字、希伯来文混合字母组成的零件号码的书写方向强制变成从右到左。

2.3 终止显式定向嵌入和重写#####

若最后一个LRE、RLE、LRO或RLO的范围未被终止,使用以下字符终止它的作用范围。

| | || |
|:----|:----|
| 缩写 | 代码点 | 名称 | 简述 |
| PDF | U+202C | POP DIRECTIONAL FORMATTING | 终止最后一个LRE、RLE、LRO或RLO的作用范围 |
  这个字符的确切含义将在对算法的讨论中解析清楚。

2.4 显式定向隔离#####

以下字符代表着一段文本从其周围文本中定向地隔离出去。它们十分类似于显式嵌入格式化字符。然而,在对周围文本的排序影响方面,「嵌入」大概等同于强字符,而「隔离」等同于中性字符,如U+FFFC OBJECT REPLACEMENT CHARACTER(对象替换字符),并在周围的文本中被分配相应的显示位置。此外,隔离内的文本不会对隔离外的文本产生影响,反之亦然。
  除了允许嵌入强方向文本而不影响周围的双向排序外,隔离格式化字符还提供额外的特性:在嵌入文本的同时,从文本的构成字符来试探性地推断嵌入文本的初始方向。
  「隔离」可以嵌套在另一个「隔离」中,也可以嵌套在「嵌入」和「重写」中。

| | || |
|:----|:----|
| 缩写 | 代码点 | 名称 | 简述 |
| LRI | U+2066 | LEFT‑TO‑RIGHT ISOLATE | 将后面的文本看作left-to-right的隔离|
| RLI | U+2067 | RIGHT‑TO‑LEFT ISOLATE | 将后面的文本看作right-to-left的隔离 |
| FSI | U+2068 | FIRST STRONG ISOLATE |将后面的文本看作隔离的 ,文本的方向由第一个强定向字符且此字符嵌套在隔离中的所决定|
  这些字符的确切含义将在对算法的讨论中解析清楚。

2.5 终止显式定向隔离#####

若最后一个LRI、RLI、或FSI的范围未被终止,使用以下字符终止它的作用范围,同时,如果上述范围内有未被终止的LRE、RLE、LRO或RLO范围,该字符也会终止它的范围。

| | || |
|:----|:----|
| 缩写 | 代码点 | 名称 | 简述 |
| PDI | U+2069 | POP DIRECTIONAL ISOLATE | 终止最后一个LRI、RLI或FSI的范围 |
  这个字符的确切含义将在对算法的讨论中解析清楚。

2.6 隐式定向标记#####

这些字符会进行非常轻量级的格式化。他们的行为很像right-to-left或left-to-right的字符,只是他们不会显示或有任何其他语义化效果。它们使用起来比使用显示嵌入或重写更方便,因为它们的范围更具局部性。

| | || |
|:----|:----|
| 缩写 | 代码点 | 名称 | 简述 |
| LRM | U+200E | LEFT-TO-RIGHT MARK | Left-to-right零宽度(zero-width)字符 |
| RLM | U+200F | RIGHT-TO-LEFT MARK | Right-to-left零宽度阿拉伯语字符 |
| ALM | U+061C | ARABIC LETTER MARK | Right-to-left零宽度阿拉伯语字符 |

在接下来的算法中,没有特别提到隐式定向标记。因为他们对双向排序的影响与强定向字符一致,唯一的区别是它们不会出现显示出来。

2.7 标记和格式化字符

显式格式字符将状态引入到纯文本,当编辑或显示文本时,必须维持文本的状态。修改文本的过程中忽视了状态,可能会不经意间影响到大部分的文本的渲染,例如移除了一个PDF标记。
  Unicode双向算法被设计成使用显式格式化字符可以等同于非内联(out-of-line)信息,如样式表信息或标记。如果标记和显式格式化字符在同一分段中均被使用时,则可能发生冲突。条件允许的话,应使用标记来代替显式格式字符。然而,任何替代表示只能通过参考此算法内相应的显式格式化字符中的行为来定义,以此确保与Unicode标准的一致性。
  HTML 5支持的双向(bidi)标记如下:

| Unicode| 等价的标记 | 描述 |
|:----|:----|
| RLI | <bdi dir = "rtl"> | |
| LRI | <bdi dir = "ltr"> | |
| FSI | <bdi> | 或 <bdi dir = "auto">|
| RLO | <bdo dir = "rtl"> | 或 <bdi dir = "auto">|
| LRO | <bdo dir = "ltr"> | |
| RLE | dir = "rtl" | 在块元素或内联元素中的属性 |
| LRE | dir = "ltr" | 在块元素或内联元素中的属性|
|PDF, PDI | 标记的终止,如</bdi> | |
  每当从含有标记的文档中产生纯文本时,就应该引入等效的格式化字符,以便不丢失正确的顺序。例如,每当剪切和粘贴的结果放在纯文本中,这个转换就应该发生。

基础显示算法

Unicode双向算法(UBA)采用文本流作为输入,并在四个主要阶段进行:<ul><li>分割成分段。算法的其余部分会分别应用于每个分段的文本。</li><li>初始化。首先初始化双向字符类型列表,列表的每个条目对应原始文本中的每个字符。每个条目的值是每个字符的Bidi_Class属性的值。然后初始化嵌入等级列表,每个字符对应一个等级。注意,原始字符会被引用在「3.3.5节 解决中性类型和隔离格式化类型」。</li><li>解决嵌入等级。对嵌入等级列表和双向字符类型列表应用一系列的规则。每一条规则对这些列表的当前值进行操作,并且可以修改这些值。原始字符和它们的Bidi_Paired_BracketBidi_Paired_Bracket_Type的属性值被应用程序的某些规则所引用。这个阶段的结果是修改了嵌入等级列表;不再需要双向字符类型列表。</li><li>重排序。每个分段中内文字进行重排序以供显示:首先,分段中的文本被打断成行,然后用已解决的嵌入等级重排序每行的文本以供显示。</li></ul>  文本重排序算法仅应用于一个分段,分段内的一个字符不会对另一个分段内的字符产生影响。分段通过分段分隔符或适当的换行函数划分(对CR,LF和CRLF的处理准则,请参阅[Unicode]的4.4节Directionality和5.8节Newline Guidelines)。分段也可以通过高级别的协议确定:例如,一个表格中不同单元格中的文本是在不同的分段中。
  在内存表示(逻辑)上,组合字符总是附在前面的基本字符上。即使经过显示重排序和执行字符整形后,在内存中,表示一个组合字符的字形仍然附着在表示其基本字符的字形上。举个例子,根据行方向和基本字体字形的放置方向,它可能附着在字形的左边,右边,或上面。

在表1中,本附件使用编号来约定规范的定义和规则。

表1. 规范的定义和规则
编号 划分
BDn* 定义(Definitions)
Pn 分段等级(Paragraph levels)
Xn 显式等级和方向(Explicit levels and directions)
Wn 弱类型(Weak types)
Nn 中性类型(Neutral types)
In 隐式等级(Implicit levels)
Ln 已解决等级(Resolved levels)

*:n代表数字。

3.1 定义#####
3.1.1 基础######

<u>BD1</u>. 双向字符类型是分配给每个Unicode字符的值,包括未被赋值的字符。在Unicode字符数据库[UCD](Unicode Character Database)中,正式的属性名是Bidi_Class
  <u>BD2</u>. 嵌入等级使用数字表示,数字的大小表明文本嵌套的深度,以及表明在该等级上文本的默认方向。文本的最小嵌入等级是0,最大显式深度是125,125作为max_depth(最大深度)在本文档的其余部分被提及。
  正如规则<u>X1</u>到<u>X8</u>指出的,嵌入等级是通过显式格式字符(「嵌入」、「隔离」,和「重写」)来设置的。数字越大表示文本嵌套越深。存在限制的原因是,提供一个明确的栈范围来限制实现以确保相同的结果。总共125个等级远超超过排序所需,即使是机械生成的格式,这也能满足;嵌入等级越深,显示变得更凌乱。
  <u>BD3</u>. 当前嵌入等级(对于讨论的字符)的默认方向被称作嵌入方向。如果嵌入等级是偶数,则方向为L,如果嵌入等级为奇数,则为方向为R
  例如,在一段特定的文本中,等级0是纯英语文本。等级1是纯阿拉伯语文本,它可能嵌入到等级0的英语文本中。等级2是英语文本,可能嵌入到等级1的阿拉伯语文本中,如此推类。除非它们的方向被重写,英语文本和数字总是偶数等级,阿拉伯文本(不包括数字在内)总是奇数等级。在对重排序算法的讨论中,嵌入等级的确切含义将会变得清晰,但下面先提供一个例子展示算法是如何工作的。
  <u>BD4</u>. 分段嵌入等级(paragraph embedding level)是确定该分段内文本的默认双向方向的嵌入等级。
  <u>BD5</u>. 分段嵌入等级的方向被称作分段方向(paragraph direction)。
    ● 在某些情况下,分段方向也被称为基础方向(base direction)。
  <u>BD6</u>. 定向重写状态(directional override status)确定字符的双向类型是否应被重置。定向重写状态是通过使用显式的定向格式化字符设置的。这个状态有三种,如表2所示。

表2. 定向重写状态
状态 说明
中性(Neutral) 当前没有有效的重写
Right-to-left 字符被重置成R
Left-to-right 字符被重置成L

<u>BD7</u>. 运行等级(level run)是指具有相同嵌入等级的字符所形成的最高子串。最高子串与其直接接触的前后字符的等级不相同(运行水平也被称作定向运行)。
  正如下面所说的,运行水平对双向算法的两个不同阶段的十分重要。第一个阶段发生在规则<u>X1</u>到<u>X9</u>之后,因为X1到X9已经给基础分段方向上的每个字符和显式定向格式化字符,分配了一个显式嵌入等级。所以在这个阶段,在规则<u>X10</u>下,运行等级被用于建立后续规则会应用的单元。后续规则会在字符的隐式双向类型和单元内的其它字符(不是单元外)的基础上,进一步调整每个字符的嵌入等级。来源于这些已解决的嵌入等级的运行等级,在后一阶段中通过规则<u>L2</u>还被用于实际文本的重排序。下面例子演示了算法中后一阶段的运行水平。

例子#####

例子:<u>在这个和以下的例子中,盒子(使用[...]代表一个盒子)被用于那些不熟悉的right-to-left字母表示不同的隐式字符类型。</u>大写字母代表right-to-left字符(例如阿拉伯语或希伯来语),小写字母代表left-to-right字符(例如英语或俄语)。

<pre><b>内  存(Memory):</b> car is THE CAR in arabic
<b>字符类型(Character types):</b> LLL-LL-RRR-RRR-LL-LLLLLL
<b>分段水平(Paragraph level):</b> 0
<b>解析水平(Resolved levels):</b> 000000011111110000000000
</pre>
  注意,THE与CAR之间的中性字符(空格)会获取周边字符的水平。通过在中性字符周围插入适当的定向标记,或者使用显式定向格式化字符,可以改变中性字符的水平。
3.1.2 匹配显式定向格式化字符
  <u>BD8</u>.字符类型LRI,RLI或FSI是一个隔离启动器(isolate initiator)。
规则<u>X5a</u>到<u>X5c</u>指明,在执行深度限制的规则允许的前提下,一个隔离启动器会提升它后面字符的嵌入水平。
  <u>BD9</u>.对于一个给定的隔离启动器,它所匹配的PDI由以下算法决定:<ul><li>初始化一个计数器</li><li>扫描隔离启动器后面的文本直到分段的结束,当遇到每个隔离启动器时,增加计数器的数字,遇到每个PDI时,减少计数器的数字。</li><li>Stop at the first PDI, if any, for which the counter is decremented to zero.</li><li>If such a PDI was found, it is the matching PDI for the given isolate initiator. Otherwise, there is no matching PDI for it.</li></ul>  注意,当寻找匹配的PDI时,应该忽略除了隔离启动器和PDI外的其余格式化字符。
  注意,该算法给一个隔离启动器分配(or lack of one)一个匹配的PDI,隔离启动器可能用于提升嵌入水平,也可能被深度限制规则限制它的提升能力。
  规则<u>X6a</u>.指明,一个匹配的PDI会将嵌入水平返回给前面那个与之相匹配的隔离启动器。该PDI本身被赋予新的嵌入水平。如果它没有匹配任何隔离启动器,或者如果隔离启动器没有提升嵌入水平,PDI的嵌入水平不发生改变。因此,一个隔离启动器和它所匹配的PDI总是被分配到相同的显式嵌入水平,which is the one outside the isolate.双向算法的后阶段,一个隔离启动器和它所匹配的PDI作为无形的中立字符,它们的嵌入水平有助于确保在排序显示方面,隔离对隔离外的文本所产生的影响相当于中性字符。
  <u>BD10</u>.字符类型LRE,RLE,LRO或RLO是一个嵌入启动器(embedding initiator)。
  注意,一个嵌入启动器会启动一个定向嵌入或一个定向重写,为了简便说明,启动器的名字省略了重写。
  规则X2到X5指明,在执行深度限制的规则允许的前提下,一个嵌入启动器会提升它后面字符的嵌入水平。
  <u>BD11</u>.对于一个给定的嵌入启动器,它所匹配的PDF由以下算法决定:<ul><li>初始化一个计数器</li><li>扫描嵌入启动器后面的文本:<ul><li>At an isolate initiator, skip past the matching PDI, or if there is no matching PDI, to the end of the paragraph.</li><li>At the end of a paragraph, or at a PDI that matches an isolate initiator whose text location is before the embedding initiator's location, stop: the embedding initiator has no matching PDF.</li></ul></li></ul>  注意,该算法给一个嵌入启动器分配 (or lack of one) 一个匹配的PDF,隔离启动器可能用于提升嵌入水平,也可能被深度限制规则限制它的提升能力。
  虽然上述算法提供了“匹配PDF(matching PDF)”的准确含义,但需要注意,实际上整体双向算法从不要求使用它来寻找匹配嵌入启动器的PDF。相反,规则X1到X7指明,嵌入启动器的范围是由PDF终止的,也就是,PDF会匹配有效的嵌入启动器。
  规则X7指明,一个匹配的PDF会将嵌入水平返回给前面那个与之相匹配的隔离启动器。如果它没有匹配任何嵌入启动器,或者如果嵌入启动器没有提升嵌入水平,PDF的嵌入水平不发生改变。规则X9指明,一旦显示双向格式化字符被用于给分段字符分配嵌入等级,嵌入启动器和PDF会被从分段中移除(or virtually removed)。因此,分配给嵌入启动器和PDF的嵌入级别与它们本身无关。在这,嵌入启动器和PDF不同于隔离启动器和PDI,它继续在确定分段的排序显示上发挥上述所属的作用。
  <u>BD12</u>.定向隔离状态(directional isolate status)是一个布尔值,通过使用隔离格式化字符来设置:当当前的嵌入水平是由隔离启动器开始时,值被设置成true。
  <u>BD13</u>.隔离运行序列(isolating run sequence)是运行水平上的一个最大序列,这样所有运行水平会将序列中的最后一个排除在外,运行的最后一个字符是隔离启动器,它所匹配的PDI是序列中下一运行水平的第一个字符。这意味着,如果序列中初始运行水平中的第一个字符是PDI的话,它是最高的,它不会匹配任何隔离启动器,如果序列中最后运行水平中的最后一个字符是隔离启动器的话,它不会匹配任何PDI。
可以通过下列算法计算出分段的隔离运行序列:<ul><li>从一个空的隔离运行序列开始。</li><li>对于分段中的每个运行水平,分段中第一个字符不是PDI,或者是PDI但它不匹配任何隔离启动器:<ul><li>创建一个新的运行水平序列,并将它初始化来包含该运行水平。</li><li>序列中的当前运行水平以一个存在匹配的PDI的隔离启动器结束,向序列追加包含匹配PDI的运行水平。(注意,这种匹配PDI必须是运行水平中的第一个字符)</li><li>将所得的运行水平序列添加到隔离运行序列中。</li></ul></li></ul>注意:<ul><li>每个分段中的运行水平只属于一个隔离运行序列。</li><li>在缺少隔离启动器的情况下,分段中的每个隔离运行序列由一个运行水平组成,每个运行水平构成一个独立的隔离运行序列。</li><li>对在同一隔离运行序列中的任意两个相邻的运行序列,以隔离启动器结束一个,以匹配隔离启动器的PDI开始另一个,这两个必须有同一个嵌入水平。因此,所有运行水平在同一隔离运行序列中有同一个嵌入水平。</li><li>当一个隔离启动器提升嵌入水平时,如果有的话,隔离启动器和它所匹配的PDI得到的是原有的嵌入水平,而不是提升后的那个。因此,如果匹配的PDI不是在分段中跟随着隔离启动器,那么在该运行水平上,隔离启动器是最后一个字符。不过它所匹配的PDI(如果存在)是运行水平中的第一个字符,在同一隔离运行序列中,它会立即跟随着隔离启动器。另一方面,跟随着分段中的隔离启动器的运行水平会开启一个新的隔离运行序列,在分段中所匹配的PDI(如果有的话)之前的运行水平结束它的隔离运行序列。</li></ul>
在以下例子中指定:<ul><li>分段的嵌入水平为0</li><li>没有字符序列标记为texti,它包含显式格式化字符或分段符</li><li>点号只用于提高例子的视觉清晰度,它们并不是文本的一部分。</li><li>嵌入水平按上述说明分配给分段文本中的字符,它们给每个例子构成一套运行水平集合。</li></ul>例1:<pre><b>分段文本:</b>text1·<b>RLE</b>·text2·<b>PDF</b>·<b>RLE</b>·text3·<b>PDF</b>·text4
<b>运行水平:</b><ul><li>|text1| – 水平0</li><li>|text2·text3| – 水平1</li><li>|text4| – 水平0</li></ul><b>隔离运行序列:</b><ul><li>|text1| – 水平0</li><li>|text2·text3| – 水平1</li><li>|text4| – 水平0</li></ul>
</pre>例2:<pre><b>分段文本:</b>text1·<b>RLI</b>·text2·<b>PDI</b>·<b>RLI</b>·text3·<b>PDI</b>·text4
<b>运行水平:</b><ul><li>|text1·<b>RLI</b>| – 水平0</li><li>|text2| – 水平1</li><li>|<b>PDI</b>·<b>RLI</b>| - 水平0</li><li>|text3 | - 水平1</li><li>|<b>PDI</b>·text4| – 水平0</li></ul><b>隔离运行序列:</b><ul><li>|text1·<b>RLI</b>||<b>PDI</b>·<b>RLI</b>||<b>PDI</b>·text4| – 水平0</li><li>|text2| – 水平1</li><li>|text3| – 水平1</li></ul>
</pre>例3:<pre><b>分段文本:</b>text1·<b>RLI</b>·text2·<b>LRI</b>·text3<b>·RLE</b>·text4·<b>PDF</b>·text5·<b>PDI</b>·text6·<b>PDI</b>·text7
<b>运行水平:</b><ul><li>|text1·<b>RLI</b>| – 水平0</li><li>|text2·<b>LRI</b>| – 水平1</li><li>|text3| - 水平2</li><li>|text4| - 水平3 </li><li>|text5| - 水平2</li><li>|<b>PDI</b>·text6| – 水平1</li><li>|<b>PDI</b>·text7| – 水平0</li></ul><b>隔离运行序列:</b><ul><li>|text1·<b>RLI</b>||<b>PDI</b>·text7| – 水平0</li><li>|text2·<b>LRI</b>||<b>PDI</b>·text6| – 水平1</li><li>|text3| - 水平2</li><li>|text4| - 水平3 </li><li>|text5| - 水平2</li>
</pre>规则<u>X10</u>指明的,一段隔离运行序列是应用规则的基本单元。在算法的这个阶段,在序列中一个运行水平的最后一个字符被认为是
紧接着序列中下一运行水平的第一个字符。由于这些规则是基于字符的隐式双向类型,隔离确实会对它周围的文本排序起到与中性字符相同的影响,更准确的说,相当于一对中性字符,隔离启动器和PDI,在规则下,它们的行为表现得像中性字符。
3.1.3 成对括号
  在Unicode字符数据库(Unicode Character Database [UCD])的BidiBrackets.txt文件中,定义了两个标准属性分别为Bidi_Paired_Bracket和Bidi_Paired_Bracket_Type,以下定义使用了这两个属性。
  <u>BD14</u>.成对括号中,一个开括号的Bidi_Paired_Bracket_Type属性值是Open,它的当前双向字符类型是ON。
  <u>BD15</u>.成对括号中,一个闭括号的Bidi_Paired_Bracket_Type属性值是Close,它的当前双向字符类型是ON。
  <u>BD16</u>.成对括号作为一对字符,它由一个开括号和一个闭括号组成,而前者或其它符合规范的等价符号的Bidi_Paired_Bracket属性值,等于后者或其它符合规范的等价符号。即使成对括号在特殊文本位置内的隔离运行序列中,在算法上能仍然能被识别出来。以下算法用于识别所有在给定的隔离运行序列中的成对括号:<ul><li>为63个元素创建一个固定大小的栈,栈内每一个元素由一个括号字符和文本位置组成。将它初始化为空。</li><li>创建一个元素列表,列表内每个元素由两个文本位置组成,一个给开括号,另一个给对应的闭括号,将列表初始化为空。</li><li>检索在逻辑顺序中隔离运行序列中的每个字符。<ul><li>如果找到一个开括号,并且栈内有空间,将开括号的Bidi_Paired_Bracket属性值和它的文本位置推进栈内。</li><li>如果找到一个开括号,但栈内没空间,停止对隔离运行序列的其他位置进行处理。</li><li>如果找到一个闭括号,按以下处理:<ol><li>声明一个变量,该变量对当前栈元素有一个引用,并用栈中的最上端元素初始化它。</li><li>将被检索的出来的闭括号或它的等效符号与在当前栈元素中的括号进行比较。</li><li>如果值相等,意味着两个字符是属于同一个成对括号,然后<ul><li>将当前栈元素的文本位置连同闭括号的文本位置添加到列表中。</li><li>通过当前栈元素弹出栈</li></ul><li>否则,如果当前栈元素不是在栈的底部,则将它提升到栈中的下一元素深度然后返回步骤2</li><li>否则,继续检索下一个字符而不弹出栈。</li></ol></li></ul></li><li>基于开括号的文本位置,对文本位置列表的成对的列表进行升序排列。</li></ul>  注意,在规则N0的分辨显式水平下的处理下,成对括号只能出现在隔离运行序列中。可参阅3.3.2 显式层次和方向
例子:
<pre>
<b>文本(text)</b> <b>配对(Pairings)</b>
<b>1 2 3 4 5 6 7 8</b>
a ) b ( c None
a ( b ] c None
a ( b ) c 2-4
a ( b [ c ) d ] 2-6
a ( b ] c ) d 2-6
a ( b ) c ) d 2-4
a ( b ( c ) d 4-6
a ( b ( c ) d ) 2-8, 4-6
a ( b { c } d ) 2-8, 4-6
</pre>3.1.4 其他缩略语
  表3列出了在示例中使用的缩写和算法中使用的内部字符类型。

表3. 示例的缩写和内部类型
符号 简述
<u>NI</u> 中性或隔离格式化字符(<u>B</u><u>S</u><u>WS</u><u>ON</u><u>FSI</u><u>LRI</u><u>RLI</u><u>PDI</u>)
<u>e</u> 匹配嵌入层次方向(偶数或奇数)的文本排序类型(L或R)。
<u>o</u> 匹配反向的嵌入层次方向(偶数或奇数)的文本排序类型(L或R)。需要注意,o与e相反。
<u>sos</u> 在一段隔离运行序列之前,分配给虚拟位置的文本排序类型(L或R)。
<u>eos</u> 在一段隔离运行序列之后,分配给虚拟位置的文本排序类型(L或R)。

3.2 双向字符类型
  在Unicode字符数据库[UCD]中,指定了每个字符的标准双向字符类型,在表4中进行了有关总结。在总结之外还存在其他特殊情况,例如,对于U+0CBF 埃纳德语元音标志,指定为L型(而不是NSM)以维持规范的等价。<ul><li>欧洲数字是指在欧洲和其他地方使用的以十进制形式表示的数字,阿拉伯-印度数字是指在阿拉伯本土使用的数字。(命名数字的更多细节请参阅章节9.2,Arabic of [Unicode])</li><li>在算法中,未被赋值的字符指定为强类型。对一般Unicode一致性要求而言,被赋值的字符是一个明确的例外。当以后字符被赋值时,这些双向类型可能会改变。对于字符类型的赋值,参阅DerivedBidiClass.txt [DerivedBIDI] in the [UCD].</li><li>私有字符可以通过一致性实现来赋不同的值。</li><li>为了贯彻双向算法,内联对象(例如图形)被视为U + FFFC对象替换字符。</li><li>然而,字符串是根据 规范等价(canonically equivalent) 来进行相等判断的。即使两个规范等价的字符串的底层 Unicode 标量不一样,只要他们的语义跟表现形式是一致的,他们就被认为是相等的。</li></ul>注:双向算法不保持兼容性等价。

表4.双向字符类型

|分类|类型|简述|范围|
|:---|:--|
|强类型(strong)|L|Left-to-Right|LRM,大部分字母,音节,汉字,非欧洲或非阿拉伯数字,...|
||R|Right-to-Left|RLM,希伯来字母和相关的标点|
||AL|Right-to-Left Arabic|ALM,阿拉伯语,它拿字母,和叙利亚字母,这些文字的特定标点,...|
|弱类型(weak)|EN|欧洲数字(European Number)|欧洲数字,东阿拉伯-印度数字,...|
||ES|欧洲数字分隔符(European Number Separator)|加号,减号|
||ET|欧洲数字终止符(European Number Terminator)|度的标志,货币符号|
||AN|阿拉伯数字(Arabic Number)|阿拉伯-印度数字,阿拉伯小数和千位分隔符,…|
||CS|常见的数字分隔符(Common Number Separator)|冒号,逗号,句号,不换行空格(no-break space),...|
||NSM|Nonspacing Mark|普通类值(General_Category)的字符:Mn(Nonspacing_Mark)和Me (Enclosing_Mark)|
||BN|中性边界(Boundary Neutral)|除了明确给出的其他类型,可忽略的默认值,非字符和控制字符|
|中性 (Neutral)|B|段落分隔符(Paragraph Separator)|段落分隔符,适当的换行符函数,更高级别的协议确定段|
||S|部分分隔符(Segment Separator)|Tab|
||WS|空格(Whitespace)|空格,图形空格,线分隔符,换页符,一般标点符号,…|
||ON|Other Neutrals|所有其他字符,包括对象替换字符|
|显式格式化(Explicit Formatting)|LRE|Left-to-Right Embedding|LRE|
||LRO|Left-to-Right Override|LRO|
||RLE|Right-to-Left Embedding|RLE|
||RLO|Right-to-Left Override|RLO|
||PDF|Pop Directional Format|PDF|
||LRI|Left-to-Right Isolate|LRI|
||RLI|Right-to-Left Isolate|RLI|
||FSI|First Strong Isolate|FSI|
||PDI|Pop Directional Isolate|PDI|
3.3 解析嵌入层次
  双向算法的主体使用双向字符类型、显式格式字符和一对括号,来产生一个用于解析层次的列表。这个解析过程包括以下步骤:<ul><li>应用规则P1,拆分文本成单独的分段,并对每个分段实施以下步骤:
</li><ul><li>应用规则P2和P3来确定分段层次。</li><li>应用规则X1(它调用规则X2-X8)来确定显式嵌入层次和方向。</li><li>进一步考虑,应用规则X9移除许多的控制字符。</li><li>应用规则X10分割分段到隔离运行序列,并对每个序列实施以下步骤:</li><ul><li>用规则W1-W7解决弱类型。</li><li>应用规则N0-N2来解决中性类型。</li><li>应用规则I1-I2解决隐式嵌入层次。</li></ul></ul></ul>3.3.1 分段层次
  P1。将文本拆分为单独的段落。一个分段分隔符属于前一个分段。对每个分段应用此算法内的其他规则。
  P2。在每一分段中,找到的第一个字符类型L,Al,或R而跳过所有在隔离启动器和它所匹配的PDI内的所有字符,或者如果它没有匹配的PDI,跳到分段的结束。
注意:<ul><li>因为在这个算法中,分段分割符用于界定文本,通过这个规则建立的字符通常是在分段分隔符之后第一个强字符,或在文本的开头</li><li>这个规则忽略在隔离启动器和它所匹配的PDI之间的字符,因为一个定向隔离对周围的文本的影响与中性字符相同,而这个规则忽略中性字符。</li><li>这个规则忽略在隔离启动器和它所匹配的PDI之间的字符,即使深度限制规则(在规则X5a到X5c中定义)阻止隔离启动器提升嵌入水平。这是为了规则更容易实现。</li><li>此规则忽略嵌入启动器(而不是在嵌入内的字符)。</li></ul>  P3。如果在P2中找到一个字符,它的类型是AL或R,则设置分段嵌入等级为1;否则,将其设为0。
  每当一个更高级别的协议指定分段等级、规则P2和P3可能会被覆盖:参阅HL1。
3.3.2 显式层次和方向
  通过应用显式等级规则X1,所有显式嵌入等级均被显式定向格式化字符(嵌入,重写和隔离)决定。对整个分段执行合乎逻辑的流程,轮流对每个字符应用规则X2到X8。在这过程使用以下变量。<ul><li>最大深度+2(max_depth+2)条目的定向状态栈内的每个条目包括:<ul><li>一个嵌入等级,最小值为0,最大值为最大深度(max_depth)。</li><li>一个定向重写状态。</li><li>一个定向隔离状态。</li></ul>除了支持具有破坏性的“弹出”操作外,栈还允许读取它的最后(即顶部)条目,而不弹出它。为了提高效率,最后一个条目可以被保存在一个单独的变量中,而不是定向状态栈上,但没有经过优化的操作(保存在定向状态栈上)能使我们更好地理解这个算法。在流程一开始时,定向状态栈初始化为一个反映分段嵌入等级的条目,并且其定向重写状态为neutral的和定向隔离状态为false;直到分段的结束,此条目才会被弹出。在流程中,定向状态栈总是包含当前位置所在的所有定向嵌入,重写和隔离-除了溢出深度限制的情况和在栈最初的分段等级条目。最后一个条目反映流程当前位置所在的最内层有效范围。实施者可能会发现这对在每个栈条目中包含更多的信息有很大帮助。例如,在一个隔离条目,隔离启动器所在的位置可能被用于创建一个映射,该映射是从每个有效隔离启动器的位置到它所匹配的PDI的位置,反之亦然。然而,这样的优化超出了本规范的范围。</li><li>一个称作溢出隔离计数(overflow isolate count)的计数器。
这个计数器反映的是,在流程中遇到的隔离启动器(且那些隔离启动器还没遇到自己所匹配的PDI)的数量,但深度限制规则会使隔离启动器无效,因此隔离启动器不会反映在定向状态栈中。而隔离启动器会一个嵌套另一个,直到栈的最后范围。这个计算用于确定一个新遇到的PDI是否匹配和终止一个溢出隔离启动器的范围,因而递减计数,而不是去匹配和终止一个有效隔离启动器,有效隔离启动器会导致从定向状态栈中弹出它的条目。这个计数器还用于确定一个新遇到的PDF是否落入溢出隔离启动器的范围,因此可以完全的忽略它(无论它是否会在同一个溢出隔离内匹配嵌入启动器)。</li><li>一个称作溢出嵌入计数(overflow embedding count)的计数器。
这个计数器反应了在流程中,遇到的隔离启动器(且还没遇到它所匹配的PDF或者嵌套在一个隔离中时还没遇到PDI)的数量,但深度限制规则会使嵌入启动器无效,因此计数器并不反应定向状态栈。而嵌入启动器会一个嵌套另一个,直到栈的最后范围。这个计算用于确定一个新遇到的PDF是否匹配和终止一个溢出嵌入启动器的范围,因而递减计数,而不是去匹配和终止一个有效嵌入启动器,有效隔离启动器会导致从定向状态栈中弹出它的条目。然而,该计数不包括在一个溢出隔离范围内遇到的嵌入启动器(即溢出隔离计数大于0的时候所遇上的)。上述这种溢出嵌入启动器(A)的范围落入溢出隔离的范围内并当碰到溢出隔离启动器变为0时,它们(A)的范围会被终止。因此,它们(A)不需要被计算。事实上,如果它们(A)在溢出嵌入计算中仍被计入的话,当遇上匹配了溢出隔离启动器的PDI时,就没有办法正确地更新计数:没有溢出范围的栈,就没有办法知道多少(如果有的话)溢出嵌入启动器落入该溢出隔离范围之内。
</li><li>一个称作有效隔离计数(valid isolate count)的计数器。
这个计数器反应了在流程中,遇到的隔离启动器(且还没遇到它所匹配的PDI或者没有遇到嵌套在其中的PDI)的数量,并已被深度限制规则判定有效,即栈中定向隔离状态为true的条目。这忽略了所有的嵌入和重写,并在没有查看定向状态栈的情况下,用于确定在流程中遇到的一个PDI(且此时溢出隔离计数值为0)是否匹配某个有效隔离启动器。当这个计数器的计数大于0时,遇上的一个PDI会终止它所匹配的隔离启动器的范围,即使嵌入和重写嵌入在它里面也是这样(这会出现在栈上或反映在溢出嵌入计数中)。</li></ul>注意,这里不需要一个有效的嵌入计数来判断流程中的PDF中是否会遇到有效的嵌入启动器。这可以通过检查定向状态堆栈上最后一个条目的定向隔离状态和栈上条目的数量来决定。如果最后一个条目的定向隔离状态为true, 则PDF位于一个定向隔离范围内。由于PDF不能匹隔离外的嵌入启动器,所以在这个隔离内不存在嵌入条目,PDF将不会匹配到任何东西。如果最后一个条目的定向隔离状态为false,而且也是栈中唯一的条目,这个条目属于分段等级,所以PDF也没有匹配到任何东西。
当在处理每一个字符时,会对变量的值进行修改,并按规则X2到X8指明的,在字符双向类型和变量当前值的基础上,对字符的显式嵌入水平进行设置。
X1.在分段的开始,执行以下步骤:<ul><li>设置栈为空。</li><li>将由分段嵌入水平、定向重写状态为neutral、定向隔离状态为false组成的一个条目推进栈中。</li><li>设置溢出隔离计数为0</li><li>设置溢出嵌入计数为0</li><li>设置有效隔离计数为0</li><li>应用规则X2到X8,反复地处理各个字符。在这个阶段中,只有从0到max_depth(最大深度)的嵌入等级为有效的。(注意:在规则L1和L2下的等级解析,可以达到max_depth+1的最大嵌入等级)</li></ul>显式嵌入
X2.对每个RLE,执行以下步骤:<ul><li>计算大于定向状态栈中最后条目的嵌入级别的最小的奇数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个RLE是有效的。将一个由新的嵌入级别、定向重写状态为neutral、定向隔离状态为false组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出RLE。如果溢出隔离计数为0,溢出嵌入计数递增1。所有其他变量不变。</li></ul>例如,溢出计数均被赋值为0,级别0→1;级别1,2→3;级别3,4→5;等等。在max_depth或者任一溢出计数不是0,级别保持不变(溢出RLE)
X3.对每个LRE,执行以下步骤:<ul><li>计算大于定向状态栈中最后条目的嵌入级别的最小的偶数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个LRE是有效的。将一个由新的嵌入级别、定向重写状态为neutral、定向隔离状态为false组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出LRE。如果溢出隔离计数为0,溢出嵌入计数递增1。所有其他变量不变。</li></ul>例如,溢出计数均被赋值为0,级别0,1→2;级别2,3→4;级别4,5→6;等等。在max_depth或者max_depth-1(which, being even, would have to go to max_depth+1),或者如果任一溢出计数非零,级别保持不变(溢出LRE)。
显式重写
显式定向重写设置嵌入级别的方式,与显式嵌入格式化字符设置方式相同,同时也将受影响的字符的双向字符类型改变成重写方向。
X4.对每个RLO,执行以下步骤:<ul><li>计算大于定向状态栈中最后条目的嵌入级别的最小的奇数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个RLO是有效的。将一个由新的嵌入级别、定向重写状态为right-to-left、定向隔离状态为false组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出RLO。如果溢出隔离计数为0,溢出嵌入计数递增1。所有其他变量不变。</li></ul>X5.对每个LRO,执行以下步骤:<ul><li>计算大于定向状态栈中最后条目的嵌入级别的最小的偶数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个LRO是有效的。将一个由新的嵌入级别、定向重写状态为left-to-right、定向隔离状态为false组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出LRO。如果溢出隔离计数为0,溢出嵌入计数递增1。所有其他变量不变。</li></ul>隔离
X5a.对每个RLI,执行以下步骤:<ul><li>把RLI的嵌入级别设置成定向状态栈中最后条目的嵌入级别。</li><li>如果定向状态栈中最后条目的定向重写状态不是neutral,若重写状态是left-to-right,把当前字符类型的RLI重置成L,若重写状态是right-to-left,则重置成R。</li><li>计算大于定向状态栈中最后条目的嵌入级别的最小的奇数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个RLI是有效的。有效隔离计数递增1,然后将一个由新的嵌入级别、定向重写状态为neutral、定向隔离状态为true组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出RLI。溢出隔离计数递增1,所有其他变量不变。</li></ul>X5b.对每个LRI,执行以下步骤:<ul><li>把LRI的嵌入级别设置成定向状态栈中最后条目的嵌入级别。</li><li>如果定向状态栈中最后条目的定向重写状态不是neutral,若重写状态是left-to-right,把当前字符类型的LRI重置成L,若重写状态是right-to-left,则重置成R。</li><li>计算大于定向状态栈中最后条目的嵌入级别的最小的偶数嵌入级别。</li><li>如果这个新级别有效,且溢出隔离计数和溢出嵌入计数均为0,那么这个LRI是有效的。有效隔离计数递增1,然后将一个由新的嵌入级别、定向重写状态为neutral、定向隔离状态为true组成的条目推进定向状态栈中。</li><li>否则,这是一个溢出LRI。溢出隔离计数递增1,所有其他变量不变。</li></ul>X5c.对在FSI和它所匹配的PDI之间的字符序列应用规则P2和P3,或者如果FSI没有匹配的PDI,则直到分段的结束,FSI与分段结束之间的字符序列表现得像分段一样。如果这些规则决定了分段嵌入水平为1,可将FSI看作RLI,并应用规则X5a. 否则,将FSI看作LRI,并应用规则X5b。
注意:新的嵌入等级没有被设置成由规则P2和P3决定的分段嵌入等级。它会提升1个或2个等级,因为它可能是一个LRI或RLI。
非格式化字符
X6. 对于所有在B,BN,RLE,LRE,RLO,LRO,PDF,RLI,LRI,FSI和PDI中的类型:<ul><li>把字当前符的嵌入等级设置成定向状态栈中最后条目的嵌入级别。</li><li>每当定向状态栈中最后条目的定向重写状态不是neutral,根据定向状态栈中最后条目的定向重写状态来重置当前字符类型。</li></ul>另一方面,如果定向状态栈中最后条目的定向重写状态是neutral,字符会保持它们原本的类型:阿拉伯字符仍是AL,拉丁字符仍是L,空格仍是WS,等等。如果定向重写状态是right-to-left,字符类型会变成R;如果定向重写状态是left-to-right,字符类型会变成L。
注意这条规则没有改变当前嵌入级别。
终止隔离
PDI终止它所匹配的隔离启动器的范围。对于在一个隔离启动器到所匹配的PDI范围内的所有嵌入启动器,如果在隔离范围内没有找到嵌入启动器所匹配的PDF,PDI同样会终止嵌入启动器的范围。这同样终止所有在匹配了隔离启动器的范围内,还没有遇到所匹配的PDF的嵌入启动器的范围。如果一个PDI没有匹配到任何隔离启动器,该PDI会被忽略。
X6a.对于每个PDI,执行以下步骤:
如果溢出隔离计数大于0,这个PDI匹配一个溢出隔离启动器。溢出隔离计数递减1.
否则,如果有效隔离计数为0,这个PDI不会匹配到任何隔离启动器(无论是有效还是溢出),所以什么都不做。
否则,这个PDI匹配一个有效隔离启动器。执行以下步骤:
溢出嵌入计数重置为0。(这会终止这种嵌入启动器的范围:那些在匹配了隔离启动器范围内,嵌入启动器还没被匹配的PDF终止从而导致缺少一个匹配的PDF的嵌入启动器)
当栈中最后一个条目的定向隔离状态为false时,弹出定向隔离状态栈中的最后一个条目。(这会终止这种有效嵌入启动器的范围:那些在匹配了隔离启动器范围内,嵌入启动器还没被匹配的PDF终止从而导致缺少一个匹配的PDF的嵌入启动器。鉴于有效隔离计数非零,在这步执行之前,定向状态栈必须具有一个定向隔离状态为true的条目,因此这步骤执行后在栈的最后一个条目的定向隔离状态为true,即代表着隔离启动器的范围。这不可能是栈的第一个条目,它总是属于分段等级,并且定向状态为false,所以在栈上有不止一个的条目。)
从定向状态栈中弹出最后的条目,有效隔离计数递减1.(这终止匹配了隔离启动器的范围。由于前一步骤留下的栈至少有两个条目,所以这个弹出后栈不会为空。)
在所有情况下,经过上述步骤后,查找定向状态栈中最后的条目:
把PDI的等级设置成条目的嵌入等级。
如果条目的定向重写状态不为neutral,当重写状态是left-to-right,把当前字符类型的PDI重置成L,当重写状态是right-to-left,把当前字符类型的PDI重置成L。
注意:赋值给一个隔离启动器的等级总是与赋值给与它匹配的PDI的等级相同。
终止嵌入和重写
一个PDF终止它所匹配的嵌入启动器的范围。如果PDF没有匹配任何嵌入启动器,它会被忽略。
X7. 对于每个PDF,执行以下步骤:
如果溢出隔离计数大于0,什么都不发生。(这个PDF是在一个溢出隔离启动器的范围内。这个PDF在该溢出隔离内,可能匹配并终止一个溢出嵌入启动器的范围,也可能不会匹配到任何嵌入启动器。)
否则,如果溢出嵌入计数大于0,计数递减1。(这个PDF匹配并终止一个不在溢出隔离启动器范围内的溢出嵌入启动器的范围。)
否则,如果定向状态栈上最后条目的定向隔离状态为false,定向状态栈包含至少两个条目,从定向状态栈中弹出最后一个条目。(这个PDF匹配和终止有效嵌入启动器的范围。由于栈至少有两个条目,所以弹出后栈不会为空。)
否则,什么都不发生。(这个PDF不会匹配任何嵌入启动器。)
分段的结束
X8.每个分段的结束会终止所有显式定向嵌入,重写和隔离。分段分隔符不包括在任何嵌入、重写或隔离中,因此由分段嵌入等级给分段分隔符赋值。

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 注:本翻译使用符号「」来突出某些可能会产生歧义的名词。目前状态:勘误中。 Unicode®标准附录#9 UNICO...
    Eriice阅读 1,969评论 0 1
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,494评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,137评论 11 349
  • 今天一大早给一供应商打电话,很客气地请他帮忙处理两个事情,对方也很客气的回应,我们也讨论了一下方案后,他说回公司安...
    Mike回到自己阅读 130评论 0 0