Java 中文官方教程 2022 版(三十)(2)

简介: Java 中文官方教程 2022 版(三十)

Java 中文官方教程 2022 版(三十)(1)https://developer.aliyun.com/article/1487956

示例用法

原文:docs.oracle.com/javase/tutorial/i18n/text/usage.html

本页面包含一些代码片段,展示了几种常见场景。

从代码点创建 String

String newString(int codePoint) {
    return new String(Character.toChars(codePoint));
}

从代码点创建 String - 为 BMP 字符进行优化

Character.toChars 方法创建一个临时数组,仅使用一次然后丢弃。如果这对性能产生负面影响,你可以使用以下针对 BMP 字符(由单个 char 值表示的字符)进行优化的方法。在这种方法中,toChars 仅用于补充字符。

String newString(int codePoint) {
    if (Character.charCount(codePoint) == 1) {
        return String.valueOf(codePoint);
    } else {
        return new String(Character.toChars(codePoint));
    }
}

批量创建 String 对象

要创建大量字符串,前面代码片段的批量版本重用了 toChars 方法使用的数组。该方法为每个代码点创建一个单独的 String 实例,并针对 BMP 字符进行了优化。

String[] newStrings(int[] codePoints) {
    String[] result = new String[codePoints.length];
    char[] codeUnits = new char[2];
    for (int i = 0; i < codePoints.length; i++) {
        int count = Character.toChars(codePoints[i], codeUnits, 0);
        result[i] = new String(codeUnits, 0, count);
    }
    return result;
}

生成消息

格式化 API 支持补充字符。以下示例是生成消息的简单方法。

// recommended
System.out.printf("Character %c is invalid.%n", codePoint);

以下方法简单且避免了连接,这使得文本更难本地化,因为并非所有语言都按照英语的顺序将数字值插入字符串中。

// not recommended
System.out.println("Character " + String.valueOf(char) + " is invalid.");

设计考虑事项

原文:docs.oracle.com/javase/tutorial/i18n/text/design.html

要编写能够无缝运行于任何语言和任何脚本的代码,有几点需要牢记。

考虑事项 原因
避免使用char数据类型的方法。 避免使用char原始数据类型或使用char数据类型的方法,因为使用该数据类型的代码对补充字符不起作用。对于需要char类型参数的方法,尽可能使用相应的int方法。例如,使用Character.isDigit(int)方法而不是Character.isDigit(char)方法。
使用isValidCodePoint方法验证代码点值。 代码点被定义为int数据类型,允许值超出从 0x0000 到 0x10FFFF 的有效代码点值范围。出于性能原因,接受代码点值作为参数的方法不会检查参数的有效性,但您可以使用isValidCodePoint方法检查该值。
使用codePointCount方法计算字符数。 String.length()方法返回字符串中代码单元或 16 位char值的数量。如果字符串包含补充字符,则计数可能会误导,因为它不会反映真实的代码点数量。要准确计算字符数(包括补充字符),请使用codePointCount方法。
使用String.toUpperCase(int codePoint)String.toLowerCase(int codePoint)方法而不是Character.toUpperCase(int codePoint)Character.toLowerCase(int codePoint)方法。 虽然Character.toUpperCase(int)Character.toLowerCase(int)方法可以处理代码点值,但有些字符无法进行一对一转换。例如,德语小写字符ß在转换为大写时变为两个字符 SS。同样,希腊语小写 Sigma 字符在字符串中的位置不同而有所不同。Character.toUpperCase(int)Character.toLowerCase(int)方法无法处理这些情况;然而,String.toUpperCaseString.toLowerCase方法可以正确处理这些情况。
删除字符时要小心。 在调用StringBuilder.deleteCharAt(int index)StringBuffer.deleteCharAt(int index)方法时,索引指向补充字符时,只会删除该字符的前半部分(第一个char值)。首先,调用Character.charCount方法对字符进行检查,以确定必须删除一个或两个char值。
在对序列中的字符进行反转时要小心。当在包含补充字符的文本上调用StringBuffer.reverse()StringBuilder.reverse()方法时,高低代理对会被反转,导致不正确甚至可能无效的代理对。

更多信息

原文:docs.oracle.com/javase/tutorial/i18n/text/info.html

有关补充字符的更多信息,请参考以下资源。

检测文本边界

原文:docs.oracle.com/javase/tutorial/i18n/text/boundaryintro.html

操纵文本的应用程序需要定位文本内的边界。例如,考虑一些文字处理器的常见功能:突出显示一个字符,剪切一个单词,将光标移动到下一个句子,以及在行尾换行一个单词。为了执行这些功能,文字处理器必须能够检测文本中的逻辑边界。幸运的是,您不必编写自己的例程来执行边界分析。相反,您可以利用BreakIterator类提供的方法。

关于 BreakIterator 类

本节讨论了BreakIterator类的实例化方法和虚拟光标。

字符边界

在本节中,您将了解用户字符和 Unicode 字符之间的区别,以及如何使用BreakIterator定位用户字符。

词边界

如果您的应用程序需要在文本中选择或定位单词,使用BreakIterator会很有帮助。

句子边界

确定句子边界可能会有问题,因为许多书面语言中句子终止符的使用是模棱两可的。本节将讨论您可能遇到的一些问题,以及BreakIterator如何处理这些问题。

行边界

本节描述了如何使用BreakIterator在文本字符串中定位潜在的换行符。

关于 BreakIterator 类

原文:docs.oracle.com/javase/tutorial/i18n/text/about.html

BreakIterator类是区域敏感的,因为文本边界随语言而变化。例如,换行的语法规则并非所有语言都相同。要确定BreakIterator类支持哪些区域设置,请调用getAvailableLocales方法,如下所示:

Locale[] locales = BreakIterator.getAvailableLocales();

您可以使用BreakIterator类分析四种边界类型:字符、单词、句子和潜在的换行符。在实例化BreakIterator时,调用适当的工厂方法:

  • getCharacterInstance
  • getWordInstance
  • getSentenceInstance
  • getLineInstance

每个BreakIterator实例只能检测一种类型的边界。例如,如果您想定位字符和单词边界,您需要创建两个单独的实例。

BreakIterator具有一个想象的光标,指向文本字符串中的当前边界。您可以使用previousnext方法在文本中移动此光标。例如,如果您使用getWordInstance创建了一个BreakIterator,每次调用next方法时,光标都会移动到文本中的下一个单词边界。光标移动方法返回一个整数,指示边界的位置。此位置是文本字符串中将跟随边界的字符的索引。与字符串索引一样,边界是从零开始的。第一个边界在 0 处,最后一个边界是字符串的长度。以下图显示了nextprevious方法在文本行中检测到的单词边界:


*此图已经缩小以适应页面。

点击图像以查看其自然大小。*

您应该仅将BreakIterator类与自然语言文本一起使用。要对编程语言进行标记化,请使用StreamTokenizer类。

接下来的部分为每种边界分析类型提供示例。编码示例来自名为BreakIteratorDemo.java的源代码文件。

字符边界

原文:docs.oracle.com/javase/tutorial/i18n/text/char.html

如果您的应用程序允许最终用户突出显示单个字符或逐个字符地移动光标穿过文本,则需要定位字符边界。要创建一个定位字符边界的BreakIterator,您可以调用getCharacterInstance方法,如下所示:

BreakIterator characterIterator =
    BreakIterator.getCharacterInstance(currentLocale);

这种类型的BreakIterator检测用户字符之间的边界,而不仅仅是 Unicode 字符。

用户字符可能由多个 Unicode 字符组成。例如,用户字符ü可以由组合 Unicode 字符\u0075(u)和\u00a8(¨)组成。然而,这并不是最好的例子,因为字符ü也可以用单个 Unicode 字符\u00fc 表示。我们将借助阿拉伯语言来举一个更现实的例子。

在阿拉伯语中,房子的单词是:


这个词包含三个用户字符,但它由以下六个 Unicode 字符组成:

String house = "\u0628" + "\u064e" + "\u064a" + "\u0652" + "\u067a" + "\u064f";

字符串house中位置 1、3 和 5 的 Unicode 字符是变音符号。阿拉伯语需要变音符号,因为它们可以改变单词的含义。示例中的变音符号是非间隔字符,因为它们出现在基本字符的上方。在阿拉伯语文字处理器中,您不能在屏幕上每个 Unicode 字符移动一次光标。相反,您必须为每个用户字符移动一次光标,这可能由多个 Unicode 字符组成。因此,您必须使用BreakIterator来扫描字符串中的用户字符。

示例程序BreakIteratorDemo创建一个BreakIterator来扫描阿拉伯字符。程序将这个BreakIterator与先前创建的String对象一起传递给名为listPositions的方法:

BreakIterator arCharIterator = BreakIterator.getCharacterInstance(
                                   new Locale ("ar","SA"));
listPositions (house, arCharIterator);

listPositions方法使用BreakIterator来定位字符串中的字符边界。请注意,BreakIteratorDemo使用setText方法将特定字符串分配给BreakIterator。程序使用first方法检索第一个字符边界,然后调用next方法,直到返回常量BreakIterator.DONE。此例程的代码如下:

static void listPositions(String target, BreakIterator iterator) {
    iterator.setText(target);
    int boundary = iterator.first();
    while (boundary != BreakIterator.DONE) {
        System.out.println (boundary);
        boundary = iterator.next();
    }
}

listPositions方法打印出字符串house中用户字符的以下边界位置。请注意,变音符号的位置(1、3、5)没有列出:

0
2
4
6

单词边界

原文:docs.oracle.com/javase/tutorial/i18n/text/word.html

您调用getWordIterator方法来实例化一个检测单词边界的BreakIterator

BreakIterator wordIterator =
    BreakIterator.getWordInstance(currentLocale);

当您的应用程序需要对单词执行操作时,您会想要创建这样一个BreakIterator。这些操作可能是常见的单词处理功能,如选择、剪切、粘贴和复制。或者,您的应用程序可能会搜索单词,并且必须能够区分整个单词和简单字符串。

BreakIterator分析单词边界时,它区分单词和不属于单词的字符。这些字符包括空格、制表符、标点符号和大多数符号,在两侧都有单词边界。

接下来的示例来自程序BreakIteratorDemo,标记了一些文本中的单词边界。该程序创建了BreakIterator,然后调用markBoundaries方法:

Locale currentLocale = new Locale ("en","US");
BreakIterator wordIterator =
    BreakIterator.getWordInstance(currentLocale);
String someText = "She stopped. " +
    "She said, \"Hello there,\" and then went " +
    "on.";
markBoundaries(someText, wordIterator);

markBoundaries方法在BreakIteratorDemo.java中定义。该方法通过在目标字符串下方打印插入符号(^)来标记边界。在接下来的代码中,请注意while循环,其中markBoundaries通过调用next方法扫描字符串:

static void markBoundaries(String target, BreakIterator iterator) {
    StringBuffer markers = new StringBuffer();
    markers.setLength(target.length() + 1);
    for (int k = 0; k < markers.length(); k++) {
        markers.setCharAt(k,' ');
    }
    iterator.setText(target);
    int boundary = iterator.first();
    while (boundary != BreakIterator.DONE) {
        markers.setCharAt(boundary,'^');
        boundary = iterator.next();
    }
    System.out.println(target);
    System.out.println(markers);
}

markBoundaries方法的输出如下。请注意插入符号(^)相对于标点符号和空格的位置:

She stopped.  She said, "Hello there," and then
^  ^^      ^^ ^  ^^   ^^^^    ^^    ^^^^  ^^   ^
went on.
^   ^^ ^^

BreakIterator类使得从文本中选择单词变得容易。您不必编写自己的处理各种语言标点规则的例程;BreakIterator类会为您处理这些。

以下示例中的extractWords方法提取并打印给定字符串的单词。请注意,该方法使用Character.isLetterOrDigit来避免打印包含空格字符的“单词”。

static void extractWords(String target, BreakIterator wordIterator) {
    wordIterator.setText(target);
    int start = wordIterator.first();
    int end = wordIterator.next();
    while (end != BreakIterator.DONE) {
        String word = target.substring(start,end);
        if (Character.isLetterOrDigit(word.charAt(0))) {
            System.out.println(word);
        }
        start = end;
        end = wordIterator.next();
    }
}

BreakIteratorDemo程序调用extractWords,将其传递给前面示例中使用的相同目标字符串。extractWords方法打印出以下单词列表:

She
stopped
She
said
Hello
there
and
then
went
on

句子边界

原文:docs.oracle.com/javase/tutorial/i18n/text/sentence.html

你可以使用BreakIterator来确定句子边界。首先通过getSentenceInstance方法创建一个BreakIterator

BreakIterator sentenceIterator =
    BreakIterator.getSentenceInstance(currentLocale);

为了显示句子边界,程序使用了markBoundaries方法,该方法在单词边界一节中有讨论。markBoundaries方法在字符串下方打印插入符号(^)来指示边界位置。以下是一些示例:

She stopped.  She said, "Hello there," and then went on.
^             ^                                         ^
He's vanished!  What will we do?  It's up to us.
^               ^                 ^             ^
Please add 1.5 liters to the tank.
^    

行边界

原文:docs.oracle.com/javase/tutorial/i18n/text/line.html

应用程序格式化文本或执行换行操作必须找到潜在的换行位置。您可以使用使用getLineInstance方法创建的BreakIterator来找到这些换行位置或边界:

BreakIterator lineIterator =
    BreakIterator.getLineInstance(currentLocale);

这个BreakIterator确定字符串中文本可以断开以继续到下一行的位置。BreakIterator检测到的位置是潜在的换行位置。在屏幕上显示的实际换行可能不同。

接下来的两个示例使用BreakIteratorDemo.javamarkBoundaries方法来显示BreakIterator检测到的行边界。markBoundaries方法通过在目标字符串下方打印插入符号(^)来指示行边界。

根据BreakIterator,在一系列空格字符(空格、制表符、换行符)的终止后发生行边界。在下面的示例中,请注意您可以在检测到的任何边界处断开行:

She stopped.  She said, "Hello there," and then went on.
^   ^         ^   ^     ^      ^     ^ ^   ^    ^    ^  ^

潜在的换行位置也会在连字符后立即发生:

There are twenty-four hours in a day.
^     ^   ^      ^    ^     ^  ^ ^   ^

下一个示例将长文本字符串分成固定长度的行,使用名为formatLines的方法。该方法使用BreakIterator来定位潜在的换行位置。formatLines方法简短、简单,并且由于使用了BreakIterator,与语言环境无关。以下是源代码:

static void formatLines(
    String target, int maxLength,
    Locale currentLocale) {
    BreakIterator boundary = BreakIterator.
        getLineInstance(currentLocale);
    boundary.setText(target);
    int start = boundary.first();
    int end = boundary.next();
    int lineLength = 0;
    while (end != BreakIterator.DONE) {
        String word = target.substring(start,end);
        lineLength = lineLength + word.length();
        if (lineLength >= maxLength) {
            System.out.println();
            lineLength = word.length();
        }
        System.out.print(word);
        start = end;
        end = boundary.next();
    }
}

BreakIteratorDemo程序调用formatLines方法如下:

String moreText =
    "She said, \"Hello there,\" and then " +
    "went on down the street. When she stopped " +
    "to look at the fur coats in a shop + "
    "window, her dog growled. \"Sorry Jake,\" " +
    "she said. \"I didn't know you would take " +
    "it personally.\"";
formatLines(moreText, 30, currentLocale);

调用formatLines的输出为:

She said, "Hello there," and
then went on down the
street. When she stopped to
look at the fur coats in a
shop window, her dog
growled. "Sorry Jake," she
said. "I didn't know you
would take it personally."

将拉丁数字转换为其他 Unicode 数字

原文:docs.oracle.com/javase/tutorial/i18n/text/shapedDigits.html

默认情况下,当文本包含数字值时,这些值将使用拉丁(欧洲)数字显示。如果希望使用其他 Unicode 数字形状,请使用java.awt.font.NumericShaper类。NumericShaper API 使您能够以任何 Unicode 数字形状显示内部表示为 ASCII 值的数字值。

下面的代码片段来自ArabicDigits示例,展示了如何使用NumericShaper实例将拉丁数字转换为阿拉伯数字。确定整形操作的行已加粗。

ArabicDigitsPanel(String fontname) {
    HashMap map = new HashMap();
    Font font = new Font(fontname, Font.PLAIN, 60);
    map.put(TextAttribute.FONT, font);
    map.put(TextAttribute.NUMERIC_SHAPING,
        NumericShaper.getShaper(NumericShaper.ARABIC));
    FontRenderContext frc = new FontRenderContext(null, false, false);
    layout = new TextLayout(text, map, frc);
}
// ...
public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D)g;
    layout.draw(g2d, 10, 50);
}

获取阿拉伯数字的NumericShaper实例,并将其放入HashMap中,用于TextLayout.NUMERIC_SHAPING属性键。哈希映射传递给TextLayout实例。在paint方法中呈现文本后,数字以所需脚本显示。在此示例中,拉丁数字 0 到 9 以阿拉伯数字形式显示。


前面的示例使用NumericShaper.ARABIC常量来检索所需的整形器,但NumericShaper类为许多语言提供了常量。这些常量被定义为位掩码,并称为NumericShaper 基于位掩码的常量

Java 中文官方教程 2022 版(三十)(3)https://developer.aliyun.com/article/1487965

相关文章
|
2月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
4天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
18天前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
98 26
|
24天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
24天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
1月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
34 2
|
24天前
|
Java 数据库连接 编译器
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
43 0
|
1月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
1月前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
1月前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编