ICU4J BiDi算法实现

本文介绍了Bidi算法的基本概念,包括BiDiLevel和BiDiRun,解释了如何使用这些概念来处理混合方向的文本。同时,文章还展示了如何利用ICU4J库中的BiDi类进行文本方向处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面以 ICU4J Bidi 算法实现为例,简要介绍 Bidi 实现中的概念和算法。

BiDi Level

一段 BiDi 文本里可以有不同的文字方向,如在 RTL 方向的文本中包含 LTR 方向的字符串,或是在 LTR 方向的文本中包含 RTL 方向的字符串,在理论上还可能多重嵌套,但一般来说不会超过两层。 BiDi 算法使用 Level 来记录文本的方向。偶数为 LTR ,奇数为 RTL ,最外层一般规定为 0 1

如下面的文本是一个地址信息,大写字母代表阿拉伯语等 RTL 方向语言,全局方向为 RTL

B ECNARTNE   25    TEERTS ELPAM

<-------------->   <->    <-------------------->

     1              2               1

 

如果这段地址信息被一个使用英语的人引用,这时全局方向为 LTR ,嵌套级别变为:

address is   B ECNARTNE    25  TEERTS ELPAM   today

<---------->  <----------------->  <->  <------------------>   <------>

0                 1            2          1                   0

通过计算每个字符的嵌套级别, BiDi 算法可以确定每个字符的文字方向,从而将逻辑顺序转换成视觉顺序,或是用于与遗留系统交换数据进行存储布局转换。

BiDi Run

BiDi Run 用来表示相同嵌套级别的字符序列,主要用途是为了避免单独记录每个字符的嵌套级别,节省内存空间。 BiDi 算法将一段文本根据嵌套级别分解为多个字符序列,同一个级别的相邻字符序列称为一个 BiDi Run BiDi Run 记录了序列的开始和结束位置、嵌套级别以及一个标志位。 BiDi Run 没有公有构造函数,只能由 BiDi 算法解析文本的时候产生,并且没有 setter 方法,成员是不能被修改的。一个 BiDi Run 对象只需占用 8 个字节,通过 BiDi Run 来记录文本嵌套级别可以减少内存使用,只有在所有 BiDi Run 的平均字符数小于 2 个的情况下使用 BiDi Run 才会比单独记录每个字符的嵌套级别占用更多内存。

算法简介

BiDi 算法实现了对输入文本的解析,构造 BiDi 对象以及对文本进行重排序,对数字及特殊字符的映射等操作。对于输入的字符串, BiDi 算法首先根据参数的设置解析每个字符的嵌套级别,可以显示设定文本的全局方向,也可以由程序自动扫描,以第一个遇到的强方向字符的方向作为文本全局方向。解析完后,每个字符都会被设置级别,并通过 BiDi Run 来记录,解析之后创建的相关数据和原始文本都保存在 BiDi 对象中。在调用重排序操作的时候, BiDi 对象根据调用参数的设置,计算每个字符的输出顺序和映射结果并依次输出。

使用 ICU4J 进行 BiDi 开发

构造函数

1. BiDi 构造函数表

函数签名

详细信息

BiDi()

默认构造函数,调用this(0,0)

BiDi(int maxLength, int maxRunCount)

以文本的最大长度和Run 的最大个数构造BiDi 对象,预先分配内存,运行时超出最大限制则出错,如果参数为0 则根据输入文本自动分配内存。

BiDi(String paragraph, int flags)

以文本和文本方向创建BiDi 对象,flags 的取值范围见表2

BiDi(AttributedCharacterIterator paragraph)

以带属性的字符迭代器创建BiDi 对象

BiDi(char[] text,int textStart,byte[] embeddings,int embStart,int paragraphLength,int flags)

以字符数组的方式创建BiDi 对象,

textStart :构造BiDi 对象的字符起始位置

embeddings :级别数组

embStart :相对开始级别

paragraphLength :文本长度

flags :文本方向

 

2. 文本方向标志 flags 参数说明

DIRECTION_LEFT_TO_RIGHT

从左到右

DIRECTION_RIGHT_TO_LEFT

从右到左

DIRECTION_DEFAULT_LEFT_TO_RIGHT

以第一个BiDi 算法规定的强方向字符的方向作为文本方向,如果没有这种字符则使用从左到右方向

DIRECTION_DEFAULT_RIGHT_TO_LEFT

以第一个BiDi 算法规定的强方向字符的方向作为文本方向,如果没有这种字符则使用从右到左方向

 

下面的代码对 BiDi 的主要函数进行了测试:

清单 1 BiDi 测试用例

   public void testBiDi(String text,int flag,int options){      

       BiDi BiDi = new BiDi(text, flag);

       byte paraLevel = BiDi.getParaLevel();

       int baseLevel = BiDi.getBaseLevel();

      

       boolean isBaseLeftToRight = BiDi.baseIsLeftToRight();

       boolean isLeftToRight = BiDi.isLeftToRight();

       boolean isRightToLeft = BiDi.isRightToLeft();

       boolean isMixed = BiDi.isMixed();

       boolean requiresBiDi = BiDi.requiresBiDi(text.toCharArray(), 0, text.length());

     

       int len = BiDi.getLength();

       int levels[] = new int[len];

       for (int i = 0; i < len; i++) {

          levels[i] = BiDi.getLevelAt(i) ;          

       }

      

       int runCount = BiDi.getRunCount();

       for (int i = 0; i < runCount; i++) {

           BiDi.getRunLevel(i);

            BiDi.getRunStart(i);

           BiDi.getRunLimit(i);

       }

      

       String writeReordered =  BiDi.writeReordered(options);

      

       StringBuffer sb = new StringBuffer(); sb.append("/n");

       sb.append("input text:" + text); sb.append("/n");

       sb.append("paraLevel:" + paraLevel); sb.append("/n");

       sb.append("baseLevel:" + baseLevel); sb.append("/n");

       sb.append("isBaseLeftToRight:" + isBaseLeftToRight); sb.append("/n");

       sb.append("isLeftToRight:" + isLeftToRight); sb.append("/n");

       sb.append("isRightToLeft:" + isRightToLeft); sb.append("/n");

       sb.append("isMixed:" + isMixed); sb.append("/n");

       sb.append("requiresBiDi:" + requiresBiDi);sb.append("/n");

       sb.append("levels:");

       for(int i = 0; i < levels.length; i++)

           sb.append(levels[i] + " ");

       sb.append("/n");

       sb.append("runCount:" + runCount); sb.append("/n");

       for(int i = 0; i < runCount; i++){

           sb.append("  run " + i + ":");

            sb.append("level-" + BiDi.getRunLevel(i));

           sb.append("  start-" + BiDi.getRunStart(i));

           sb.append("  limit-" + BiDi.getRunLimit(i));

           sb.append("/n");

       }     

       sb.append("writeReordered(" + options + "):"+ writeReordered);

      

       System.out.println(sb.toString());

}

 

writeReordered 函数根据 BiDi 对象设置的参数对文本进行重排序,该函数的 options 参数说明如表 3

3. options 参数 说明

DO_MIRRORING

RTL 方向的BiDi Run 中用镜像字符替换原字符,但是有的字符在Unicode 里并没有镜像字符

INSERT_LRM_FOR_NUMERIC

在必要的时候插入LRM

KEEP_BASE_COMBINING

RTL 方向的BiDi Run 中保持组合的字符在基本字符之后

OUTPUT_REVERSE

以逆向顺序输出字符

REMOVE_BIDI_CONTROLS

移除BiDi 控制字符,不影响INSERT_LRM_FOR_NUMERIC

OPTION_STREAMING

将输出作为一个未结束的流处理,指明是一个大文本的一部分,只有在最后一部分的时候关闭选项

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值