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

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

教程:处理文本

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

几乎所有带有用户界面的程序都会操作文本。在国际市场上,您的程序显示的文本必须符合世界各地语言的规则。Java 编程语言提供了许多类来帮助您以与区域设置无关的方式处理文本。

检查字符属性

本节解释了如何使用Character比较方法来检查所有主要语言的字符属性。

比较字符串

在本节中,您将学习如何使用Collator类执行与区域设置无关的字符串比较。

检测文本边界

本节展示了BreakIterator类如何检测字符、单词、句子和行边界。

转换非 Unicode 文本

世界各地的不同计算机系统使用各种编码方案存储文本。本节描述了帮助您在 Unicode 和其他编码之间转换文本的类。

规范化器的 API

本节解释了如何使用规范化器的 API 来应用不同的规范化形式转换文本。

使用 JTextComponent 类处理双向文本

本节讨论了如何处理双向文本,即包含从左到右和从右到左两个方向的文本。

检查字符属性

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

您可以根据字符的属性对字符进行分类。例如,X 是大写字母,4 是十进制数字。检查字符属性是验证最终用户输入的数据的常见方法。例如,如果您在网上销售书籍,订单输入屏幕应验证数量字段中的字符是否都是数字。

不习惯编写全球软件的开发人员可能会通过将其与字符常量进行比较来确定字符的属性。例如,他们可能会编写如下代码:

char ch;
//...

// This code is WRONG!

// check if ch is a letter
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
    // ...

// check if ch is a digit
if (ch >= '0' && ch <= '9')
    // ...

// check if ch is a whitespace
if ((ch == ' ') || (ch =='\n') || (ch == '\t'))
    // ...

前面的代码是错误的,因为它只适用于英语和少数其他语言。要使前面的示例国际化,将其替换为以下语句:

char ch;
// ...

// This code is OK!

if (Character.isLetter(ch))
    // ...

if (Character.isDigit(ch))
    // ...

if (Character.isSpaceChar(ch))
    // ...

Character方法依赖于 Unicode 标准来确定字符的属性。Unicode 是一种支持世界主要语言的 16 位字符编码。在 Java 编程语言中,char值表示 Unicode 字符。如果您使用适当的Character方法检查char的属性,则您的代码将适用于所有主要语言。例如,如果字符是中文、德文、阿拉伯文或其他语言中的字母,则Character.isLetter方法返回true

以下列表列出了一些最有用的Character比较方法。Character API 文档完全指定了这些方法。

  • isDigit

  • isLetter

  • isLetterOrDigit

  • isLowerCase

  • isUpperCase

  • isSpaceChar

  • isDefined

Character.getType方法返回字符的 Unicode 类别。每个类别对应于Character类中定义的常量。例如,对于字符 A,getType返回Character.UPPERCASE_LETTER常量。有关getType返回的类别常量的完整列表,请参阅Character API 文档。以下示例显示了如何使用getTypeCharacter类别常量。这些if语句中的所有表达式都为true

if (Character.getType('a') == Character.LOWERCASE_LETTER)
    // ...

if (Character.getType('R') == Character.UPPERCASE_LETTER)
    // ...

if (Character.getType('>') == Character.MATH_SYMBOL)
    // ...

if (Character.getType('_') == Character.CONNECTOR_PUNCTUATION)
    // ...

比较字符串

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

应用程序对文本进行排序时会执行频繁的字符串比较。例如,报告生成器在对字符串列表按字母顺序排序时执行字符串比较。

如果您的应用程序受众仅限于讲英语的人,您可能可以使用String.compareTo方法执行字符串比较。String.compareTo方法对两个字符串中的 Unicode 字符执行二进制比较。然而,对于大多数语言,这种二进制比较不能被依赖来排序字符串,因为 Unicode 值不对应字符的相对顺序。

幸运的是,Collator类允许您的应用程序为不同语言执行字符串比较。在本节中,您将学习如何在排序文本时使用Collator类。

执行与语言环境无关的比较

排序规则定义字符串的排序顺序。这些规则因语言环境而异,因为各种自然语言对单词的排序方式不同。使用Collator类提供的预定义排序规则,您可以以与语言环境无关的方式对字符串进行排序。

自定义排序规则

在某些情况下,Collator类提供的预定义排序规则可能不适用于您。例如,您可能希望对不受Collator支持的语言的字符串进行排序。在这种情况下,您可以定义自己的排序规则,并将其分配给RuleBasedCollator对象。

提高排序性能

使用CollationKey类,您可以提高字符串比较的效率。该类将String对象转换为遵循给定Collator规则的排序键。

执行与 Locale 无关的比较

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

排序规则定义字符串的排序顺序。这些规则因区域而异,因为各种自然语言对单词的排序方式不同。您可以使用Collator类提供的预定义排序规则以与区域设置无关的方式对字符串进行排序。

要实例化Collator类,请调用getInstance方法。通常,您为默认的Locale创建一个Collator,如下例所示:

Collator myDefaultCollator = Collator.getInstance();

创建Collator时,您还可以指定特定的Locale,如下所示:

Collator myFrenchCollator = Collator.getInstance(Locale.FRENCH);

getInstance方法返回一个RuleBasedCollator,它是Collator的具体子类。RuleBasedCollator包含一组规则,这些规则确定您指定的区域设置的字符串排序顺序。这些规则对于每个区域设置都是预定义的。由于规则封装在RuleBasedCollator中,您的程序不需要特殊的例程来处理排序规则随语言变化的方式。

您调用Collator.compare方法执行与 Locale 无关的字符串比较。当第一个字符串参数小于、等于或大于第二个字符串参数时,compare方法返回小于零、等于零或大于零的整数。以下表格包含一些对Collator.compare的示例调用:

示例 返回值 解释
myCollator.compare("abc", "def") -1 "abc"小于"def"
myCollator.compare("rtf", "rtf") 0 两个字符串相等
myCollator.compare("xyz", "abc") 1 "xyz"大于"abc"

在执行排序操作时,您使用compare方法。名为CollatorDemo的示例程序使用compare方法对英语和法语单词数组进行排序。该程序展示了使用两个不同的排序器对相同单词列表进行排序时可能发生的情况:

Collator fr_FRCollator = Collator.getInstance(new Locale("fr","FR"));
Collator en_USCollator = Collator.getInstance(new Locale("en","US"));

排序方法称为sortStrings,可以与任何Collator一起使用。请注意,sortStrings方法调用compare方法:

public static void sortStrings(Collator collator, String[] words) {
    String tmp;
    for (int i = 0; i < words.length; i++) {
        for (int j = i + 1; j < words.length; j++) { 
            if (collator.compare(words[i], words[j]) > 0) {
                tmp = words[i];
                words[i] = words[j];
                words[j] = tmp;
            }
        }
    }
}

英语Collator按以下方式对单词进行排序:

peach
péché
pêche
sin

根据法语语言的排序规则,前述列表的顺序是错误的。在法语中,"péché"应该在排序列表中跟在"pêche"之后。法语Collator正确地对单词数组进行排序,如下所示:

peach
pêche
péché
sin

定制整理规则

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

前一节讨论了如何使用区域设置的预定义规则来比较字符串。这些整理规则确定字符串的排序顺序。如果预定义的整理规则不符合您的需求,您可以设计自己的规则并将其分配给RuleBasedCollator对象。

定制的整理规则包含在传递给RuleBasedCollator构造函数的String对象中。这里是一个简单的例子:

String simpleRule = "< a < b < c < d";
RuleBasedCollator simpleCollator =  new RuleBasedCollator(simpleRule);

对于前面示例中的simpleCollator对象,a小于bb小于c,依此类推。当比较字符串时,simpleCollator.compare方法引用这些规则。用于构造整理规则的完整语法比这个简单示例更灵活和复杂。有关语法的完整描述,请参考RuleBasedCollator类的 API 文档。

接下来的示例使用两个整理器对一组西班牙语单词进行排序。此示例的完整源代码在RulesDemo.java中。

RulesDemo程序首先定义了英语和西班牙语的整理规则。该程序将按传统方式对西班牙语单词进行排序。按照传统规则排序时,字母 ch 和 ll 及其大写形式各自在排序顺序中有自己的位置。这些字符对比较就好像它们是一个字符一样。例如,ch 按照一个字母排序,在排序顺序中紧随 cz。请注意两个整理器的规则如何不同:

String englishRules = (
    "< a,A < b,B < c,C < d,D < e,E < f,F " +
    "< g,G < h,H < i,I < j,J < k,K < l,L " +
    "< m,M < n,N < o,O < p,P < q,Q < r,R " +
    "< s,S < t,T < u,U < v,V < w,W < x,X " +
    "< y,Y < z,Z");

String smallnTilde = new String("\u00F1");    // ñ
String capitalNTilde = new String("\u00D1");  // Ñ

String traditionalSpanishRules = (
    "< a,A < b,B < c,C " +
    "< ch, cH, Ch, CH " +
    "< d,D < e,E < f,F " +
    "< g,G < h,H < i,I < j,J < k,K < l,L " +
    "< ll, lL, Ll, LL " +
    "< m,M < n,N " +
    "< " + smallnTilde + "," + capitalNTilde + " " +
    "< o,O < p,P < q,Q < r,R " +
    "< s,S < t,T < u,U < v,V < w,W < x,X " +
    "< y,Y < z,Z");

以下代码行创建整理器并调用排序例程:

try {
    RuleBasedCollator enCollator = new RuleBasedCollator(englishRules);
    RuleBasedCollator spCollator =
        new RuleBasedCollator(traditionalSpanishRules);

    sortStrings(enCollator, words);
    printStrings(words);
    System.out.println();

    sortStrings(spCollator, words);
    printStrings(words);
} catch (ParseException pe) {
    System.out.println("Parse exception for rules");
}

名为sortStrings的排序例程是通用的。它将根据任何Collator对象的规则对任何单词数组进行排序:

public static void sortStrings(Collator collator, String[] words) {
    String tmp;
    for (int i = 0; i < words.length; i++) {
        for (int j = i + 1; j < words.length; j++) {
            if (collator.compare(words[i], words[j]) > 0) {
                tmp = words[i];
                words[i] = words[j];
                words[j] = tmp;
            }
        }
    }
}

使用英语整理规则排序时,单词数组如下:

chalina
curioso
llama
luz

将前面的列表与以下按照传统西班牙整理规则排序的列表进行比较:

curioso
chalina
luz
llama

提高排序性能

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

对长字符串列表进行排序通常是耗时的。如果您的排序算法重复比较字符串,可以通过使用CollationKey类加快该过程。

一个CollationKey对象代表给定StringCollator的排序键。比较两个CollationKey对象涉及对排序键进行按位比较,比使用Collator.compare方法比较String对象更快。然而,生成CollationKey对象需要时间。因此,如果一个String只需比较一次,Collator.compare提供更好的性能。

接下来的示例使用CollationKey对象对单词数组进行排序。此示例的源代码在KeysDemo.java中。

KeysDemo程序在main方法中创建一个CollationKey对象数组。要创建CollationKey,需要在Collator对象上调用getCollationKey方法。除非两个CollationKey对象来自同一个Collator,否则无法比较它们。main方法如下:

static public void main(String[] args) {
    Collator enUSCollator = Collator.getInstance(new Locale("en","US"));
    String [] words = {
        "peach",
        "apricot",
        "grape",
        "lemon"
    };

    CollationKey[] keys = new CollationKey[words.length];

    for (int k = 0; k < keys.length; k ++) {
        keys[k] = enUSCollator. getCollationKey(words[k]);
    }

    sortArray(keys);
    printArray(keys);
}

sortArray方法调用CollationKey.compareTo方法。compareTo方法返回一个整数,如果keys[i]对象小于、等于或大于keys[j]对象,则返回小于零、等于零或大于零。请注意,程序比较的是CollationKey对象,而不是原始单词数组中的String对象。以下是sortArray方法的代码:

public static void sortArray(CollationKey[] keys) {
    CollationKey tmp;

    for (int i = 0; i < keys.length; i++) {
        for (int j = i + 1; j < keys.length; j++) {
            if (keys[i].compareTo(keys[j]) > 0) {
                tmp = keys[i];
                keys[i] = keys[j];
                keys[j] = tmp; 
            }
        }
    }
}

KeysDemo程序对CollationKey对象数组进行排序,但最初的目标是对String对象数组进行排序。为了检索每个CollationKeyString表示形式,程序在displayWords方法中调用getSourceString,如下所示:

static void displayWords(CollationKey[] keys) {
    for (int i = 0; i < keys.length; i++) {
        System.out.println(keys[i].getSourceString());
    }
}

displayWords方法打印以下行:

apricot
grape
lemon
peach

Unicode

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

Unicode 是一个计算行业标准,旨在一致且独特地编码世界各地书面语言中使用的字符。Unicode 标准使用十六进制表示字符。例如,值 0x0041 表示拉丁字符 A。Unicode 标准最初设计时使用 16 位来编码字符,因为主要的计算机是 16 位的个人电脑。

在创建 Java 语言规范时,接受了 Unicode 标准,并将 char 原始数据类型定义为 16 位数据类型,字符的十六进制范围为 0x0000 到 0xFFFF。

由于 16 位编码支持 2¹⁶(65,536)个字符,这不足以定义世界各地使用的所有字符,因此将 Unicode 标准扩展到 0x10FFFF,支持超过一百万个字符。在 Java 编程语言中,字符的定义无法从 16 位更改为 32 位,否则会导致数百万个 Java 应用程序无法正常运行。为了纠正这一定义,开发了一种方案来处理无法用 16 位编码的字符。

值在 16 位范围之外,且在 0x10000 到 0x10FFFF 范围内的字符被称为补充字符,并被定义为一对 char 值。

本课程包括以下部分:

  • 术语 – 解释了代码点和其他术语。

  • 补充字符作为代理 – 使用 16 位代理实现无法作为单个原始 char 数据类型实现的补充字符。

  • 字符和字符串 API – 列出了与 CharacterString 和相关类相关的 API。

  • 示例用法 – 提供了几个有用的代码片段。

  • 设计考虑 – 要牢记的设计考虑,以确保您的应用程序能够与任何语言脚本一起工作。

  • 更多信息 – 提供了更多资源列表。

术语

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

字符是具有语义值的最小文本单位。

字符集是一组可能被多种语言使用的字符集合。例如,拉丁字符集被英语和大多数欧洲语言使用,而希腊字符集仅被希腊语使用。

编码字符集是一个字符集,其中每个字符被分配一个唯一的数字。

代码点是可以在编码字符集中使用的值。代码点是一个 32 位的int数据类型,其中低 21 位表示有效的代码点值,而高 11 位为 0。

Unicode 的代码单元是一个 16 位的char值。例如,想象一个包含字母"abc"后跟 Deseret 长 I 的String,它用两个char值表示。该字符串包含四个字符,四个代码点,但五个代码单元。

要在 Unicode 中表示一个字符,十六进制值前缀为字符串 U+。Unicode 标准的有效代码点范围是 U+0000 到 U+10FFFF,包括两端。拉丁字符 A 的代码点值为 U+0041。代表欧元货币的字符€,其代码点值为 U+20AC。Deseret 字母表中的第一个字母,长 I,其代码点值为 U+10400。

以下表格显示了几个字符的代码点值:

字符 Unicode 代码点 符号
拉丁 A U+0041 拉丁字符 A
拉丁尖 S U+00DF 拉丁小写尖 S
东方汉字 U+6771 表示东方、东部或向东的汉字
Deseret, LONG I U+10400 长 I 的 Deseret 大写字母

如前所述,范围在 U+10000 到 U+10FFFF 之间的字符称为补充字符。从 U+0000 到 U+FFFF 的字符集有时被称为基本多语言平面(BMP)

更多术语可以在更多信息页面上列出的Unicode 术语词汇表中找到。

补充字符作为代理

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

为了支持补充字符而不改变char原始数据类型并导致与之前的 Java 程序不兼容,补充字符被定义为一对称为代理的代码点值。 第一个代码点来自U+D800U+DBFF高代理范围,第二个代码点来自U+DC00U+DFFF低代理范围。 例如,Deseret 字符 LONG I,U+10400,是用这对代理值定义的:U+D801U+DC00

字符和字符串 API

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

Character 类封装了 char 数据类型。对于 J2SE 发布的第 5 版,许多方法被添加到 Character 类中以支持补充字符。这个 API 分为两类:将 char 和代码点值之间进行转换的方法以及验证或映射代码点的方法。

本节描述了 Character 类中可用方法的一个子集。有关可用 API 的完整列表,请参阅 Character 类规范。

转换方法和 Character 类

以下表格包括了 Character 类中最有用的转换方法或便于转换的方法。codePointAtcodePointBefore 方法包含在此列表中,因为文本通常以序列的形式出现,比如一个 String,这些方法可以用来提取所需的子字符串。

方法 描述
toChars(int codePoint, char[] dst, int dstIndex) toChars(int codePoint) 将指定的 Unicode 代码点转换为其 UTF-16 表示,并将其放入一个 char 数组中。示例用法:Character.toChars(0x10400)
toCodePoint(char high, char low) 将指定的代理对转换为其补充代码点值。

| codePointAt(char[] a, int index) codePointAt(char[] a, int index, int limit)

codePointAt(CharSequence seq, int index) | 返回指定索引处的 Unicode 代码点。第三个方法接受一个 CharSequence,第二个方法对索引施加了上限。 |

| codePointBefore(char[] a, int index) codePointBefore(char[] a, int index, int start)

codePointBefore(CharSequence seq, int index)

codePointBefore(char[], int, int) | 返回指定索引之前的 Unicode 代码点。第三个方法接受CharSequence,其他方法接受char数组。第二个方法对索引强制执行下限。 |

charCount(int codePoint) 对于可以由单个char表示的字符,返回值为 1。对于需要两个char表示的补充字符,返回值为 2。

字符类中的验证和映射方法

以前使用char原始数据类型的一些方法,如isLowerCase(char)isDigit(char),已被支持补充字符的方法所取代,如isLowerCase(int)isDigit(int)。以前的方法得到支持,但不适用于补充字符。为了创建一个全球应用程序并确保您的代码与任何语言无缝配合,建议您使用这些方法的新形式。

请注意,出于性能原因,大多数接受代码点的方法不会验证代码点参数的有效性。您可以使用isValidCodePoint方法进行验证。

以下表格列出了Character类中的一些验证和映射方法。

方法 描述
isValidCodePoint(int codePoint) 如果代码点在 0x0000 到 0x10FFFF(包括)范围内,则返回 true。
isSupplementaryCodePoint(int codePoint) 如果代码点在 0x10000 到 0x10FFFF(包括)范围内,则返回 true。
isHighSurrogate(char) 如果指定的char在\uD800 到\uDBFF(包括)的高代理范围内,则返回 true。
isLowSurrogate(char) 如果指定的char在\uDC00 到\uDFFF(包括)的低代理范围内,则返回 true。
isSurrogatePair(char high, char low) 如果指定的高代理和低代理代码值表示有效的代理对,则返回 true。
codePointCount(CharSequence, int, int) codePointCount(char[], int, int) 返回CharSequencechar数组中的 Unicode 代码点数。
isLowerCase(int) isUpperCase(int) 如果指定的 Unicode 代码点是小写或大写字符,则返回 true。
isDefined(int) 如果指定的 Unicode 代码点在 Unicode 标准中定义,则返回 true。
isJavaIdentifierStart(char) isJavaIdentifierStart(int) 如果指定的字符或 Unicode 代码点可作为 Java 标识符中的第一个字符,则返回 true。

| isLetter(int) isDigit(int)

isLetterOrDigit(int) | 如果指定的 Unicode 代码点是字母、数字或字母数字,则返回 true。 |

getDirectionality(int) 返回给定 Unicode 代码点的 Unicode 方向性属性。
Character.UnicodeBlock.of(int codePoint) 返回表示包含给定 Unicode 代码点的 Unicode 块的对象,如果代码点不是已定义块的成员,则返回null

字符串类中的方法

StringStringBufferStringBuilder 类也有构造函数和方法,可以处理补充字符。以下表格列出了一些常用方法。要查看可用 API 的完整列表,请参阅 StringStringBufferStringBuilder 类的 javadoc。

构造函数或方法 描述
String(int[] codePoints, int offset, int count) 分配一个新的 String 实例,其中包含来自 Unicode 代码点数组的子数组的字符。

| String.codePointAt(int index) StringBuffer.codePointAt(int index)

StringBuilder.codePointAt(int index) | 返回指定索引处的 Unicode 代码点。 |

| String.codePointBefore(int index) StringBuffer.codePointBefore(int index)

StringBuilder.codePointBefore(int index) | 返回指定索引之前的 Unicode 代码点。 |

| String.codePointCount(int beginIndex, int endIndex) StringBuffer.codePointCount(int beginIndex, int endIndex)

StringBuilder.codePointCount(int beginIndex, int endIndex) | 返回指定范围内的 Unicode 代码点数。 |

StringBuffer.appendCodePoint(int codePoint) StringBuilder.appendCodePoint(int codePoint) 将指定代码点的字符串表示附加到序列。

| String.offsetByCodePoints(int index, int codePointOffset) StringBuffer.offsetByCodePoints(int index, int codePointOffset)

StringBuilder.offsetByCodePoints(int index, int codePointOffset) | 返回给定索引偏移给定代码点数量后的索引。 |

示例用法

原文: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 以阿拉伯数字形式显示。

ArabicDigits 示例输出显示从 0 到 9 的阿拉伯数字

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

基于枚举的范围常量

指定特定数字集的另一种方法是使用NumericShaper.Range枚举类型(enum)。这个枚举在 Java SE 7 版本中引入,还提供了一组常量。尽管这些常量是使用不同的机制定义的,但NumericShaper.ARABIC位掩码在功能上等同于NumericShaper.Range.ARABIC枚举,并且每种常量类型都有相应的getShaper方法:

ArabicDigitsEnum示例与 ArabicDigits 示例相同,只是使用NumericShaper.Range枚举来指定语言脚本:

ArabicDigitsEnumPanel(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.Range.ARABIC));
    FontRenderContext frc = new FontRenderContext(null, false, false);
    layout = new TextLayout(text, map, frc);
}

两个getShaper方法都接受singleRange参数。使用任一常量类型,你都可以指定一组特定于脚本的数字范围。基于位掩码的常量可以使用OR操作符组合,或者你可以创建一组NumericShaper.Range枚举。以下显示了如何使用每种常量类型定义范围:

NumericShaper.MONGOLIAN | NumericShaper.THAI |
NumericShaper.TIBETAN
EnumSet.of(
    NumericShaper.Range.MONGOLIAN,
    NumericShaper.Range.THAI,
    NumericShaper.Range.TIBETAN)

你可以查询NumericShaper对象,以确定它支持哪些范围,使用基于位掩码的整形器的getRanges方法或基于枚举的整形器的getRangeSet方法。


注意:

你可以使用传统的基于位掩码的常量或Range枚举常量。在决定使用哪种时,有一些考虑因素:

  • Range API 需要 JDK 7 或更高版本。

  • Range API 覆盖的 Unicode 范围比位掩码 API 多。

  • 位掩码 API 比Range API 快一点。


根据语言环境渲染数字

ArabicDigits 示例旨在使用特定语言的整形器,但有时必须根据语言环境渲染数字。例如,如果数字之前的文本使用泰语书写,则优先使用泰语数字。如果文本显示为藏文,则优先使用藏文数字。

你可以使用其中一个getContextualShaper方法来实现这一点。

前两种方法使用位掩码常量,后两种使用枚举常量。接受defaultContext参数的方法允许你指定在数字值显示在文本之前时使用的初始整形器。当没有定义默认上下文时,任何前导数字都将使用拉丁形状显示。

ShapedDigits 示例展示了整形器的工作原理。显示了五种文本布局:

  1. 第一个布局不使用整形器;所有数字都显示为拉丁文。

  2. 第二个布局将所有数字形状为阿拉伯数字,不考虑语言环境。

  3. 第三个布局使用了一个使用阿拉伯数字的上下文整形器。默认上下文被定义为阿拉伯文。

  4. 第四个布局使用了一个使用阿拉伯数字的上下文形状器,但该形状器没有指定默认上下文。

  5. 第五个布局使用了一个使用ALL_RANGES位掩码的上下文形状器,但该形状器没有指定默认上下文。

ShapedDigits 示例输出,展示上下文形状器的工作原理

以下代码行展示了如果使用形状器,则如何定义:

  1. 没有使用形状器。

  2. NumericShaper arabic = NumericShaper.getShaper(NumericShaper.ARABIC);

  3. NumericShaper contextualArabic = NumericShaper.getContextualShaper(NumericShaper.ARABIC, NumericShaper.ARABIC);

  4. NumericShaper contextualArabicASCII = NumericShaper.getContextualShaper(NumericShaper.ARABIC);

  5. NumericShaper contextualAll = NumericShaper.getContextualShaper(NumericShaper.ALL_RANGES);

查看ShapedDigits.java示例以获取更多实现细节。

转换非 Unicode 文本

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

在 Java 编程语言中,char值表示 Unicode 字符。Unicode 是一种支持世界主要语言的 16 位字符编码。您可以在Unicode 联盟网站了解有关 Unicode 标准的更多信息。

目前很少有文本编辑器支持 Unicode 文本输入。我们用来编写本节代码示例的文本编辑器仅支持 ASCII 字符,这些字符仅限于 7 位。为了表示 ASCII 无法表示的 Unicode 字符,例如ö,我们使用\uXXXX转义序列。转义序列中的每个X都是一个十六进制数字。以下示例显示了如何使用转义序列表示ö字符:

String str = "\u00F6";
char c = '\u00F6';
Character letter = new Character('\u00F6');

世界各地的系统使用各种字符编码。目前,很少有这些编码符合 Unicode 标准。因为您的程序期望字符为 Unicode 格式,所以从系统获取的文本数据必须转换为 Unicode 格式,反之亦然。当文本文件的编码与 Java 虚拟机的默认文件编码匹配时,文本数据会自动转换为 Unicode 格式。您可以通过创建一个OutputStreamWriter并询问其规范名称来确定默认文件编码:

OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream());
System.out.println(out.getEncoding());

如果默认文件编码与您要处理的文本数据的编码不同,则必须自行进行转换。当处理来自其他国家或计算平台的文本时,您可能需要这样做。

本节讨论了用于将非 Unicode 文本转换为 Unicode 的 API。在使用这些 API 之前,您应该验证要转换为 Unicode 的字符编码是否受支持。支持的字符编码列表不是 Java 编程语言规范的一部分。因此,API 支持的字符编码可能因平台而异。要查看 Java 开发工具包支持的编码,请参阅支持的编码文档。

接下来的内容描述了将非 Unicode 文本转换为 Unicode 的两种技术。您可以将非 Unicode 字节数组转换为String对象,反之亦然。或者您可以在 Unicode 字符流和非 Unicode 文本的字节流之间进行转换。

字节编码和字符串

本节向您展示如何将非 Unicode 字节数组转换为String对象,反之亦然。

字符流和字节流

在本节中,您将学习如何在 Unicode 字符流和非 Unicode 文本的字节流之间进行转换。

字节编码和字符串

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

如果字节数组包含非 Unicode 文本,您可以使用 String 构造方法之一将文本转换为 Unicode。反之,您可以使用 String.getBytes 方法将 String 对象转换为非 Unicode 字符的字节数组。在调用这些方法时,您需要将编码标识符作为参数之一指定。

接下来的示例将字符在 UTF-8 和 Unicode 之间进行转换。UTF-8 是一种对 UNIX 文件系统安全的 Unicode 传输格式。示例的完整源代码在文件 StringConverter.java 中。

StringConverter 程序首先创建一个包含 Unicode 字符的 String

String original = new String("A" + "\u00ea" + "\u00f1" + "\u00fc" + "C");

当打印时,名为 originalString 如下所示:

AêñüC

要将 String 对象转换为 UTF-8,调用 getBytes 方法并指定适当的编码标识符作为参数。getBytes 方法以 UTF-8 格式返回一个字节数组。要从非 Unicode 字节的数组创建 String 对象,请使用带有编码参数的 String 构造方法。执行这些调用的代码被封装在一个 try 块中,以防指定的编码不受支持:

try {
    byte[] utf8Bytes = original.getBytes("UTF8");
    byte[] defaultBytes = original.getBytes();

    String roundTrip = new String(utf8Bytes, "UTF8");
    System.out.println("roundTrip = " + roundTrip);
    System.out.println();
    printBytes(utf8Bytes, "utf8Bytes");
    System.out.println();
    printBytes(defaultBytes, "defaultBytes");
} 
catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

StringConverter 程序打印出 utf8BytesdefaultBytes 数组中的值,以演示一个重要的观点:转换后的文本长度可能与源文本长度不同。一些 Unicode 字符转换为单个字节,而另一些转换为一对或三元组字节。

printBytes 方法通过调用在源文件 UnicodeFormatter.java 中定义的 byteToHex 方法来显示字节数组。以下是 printBytes 方法:

public static void printBytes(byte[] array, String name) {
    for (int k = 0; k < array.length; k++) {
        System.out.println(name + "[" + k + "] = " + "0x" +
            UnicodeFormatter.byteToHex(array[k]));
    }
}

printBytes 方法的输出如下。请注意,只有第一个和最后一个字节,即 A 和 C 字符,在两个数组中是相同的:

utf8Bytes[0] = 0x41
utf8Bytes[1] = 0xc3
utf8Bytes[2] = 0xaa
utf8Bytes[3] = 0xc3
utf8Bytes[4] = 0xb1
utf8Bytes[5] = 0xc3
utf8Bytes[6] = 0xbc
utf8Bytes[7] = 0x43
defaultBytes[0] = 0x41
defaultBytes[1] = 0xea
defaultBytes[2] = 0xf1
defaultBytes[3] = 0xfc
defaultBytes[4] = 0x43

字符流和字节流

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

java.io包提供了允许您在 Unicode 字符流和非 Unicode 文本的字节流之间进行转换的类。使用InputStreamReader类,您可以将字节流转换为字符流。您可以使用OutputStreamWriter类将字符流转换为字节流。以下图示说明了转换过程:

此图表示转换过程

当您创建InputStreamReaderOutputStreamWriter对象时,您需要指定要转换的字节编码。例如,要将 UTF-8 编码的文本文件转换为 Unicode,您可以创建一个InputStreamReader如下:

FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF8");

如果省略编码标识符,InputStreamReaderOutputStreamWriter将依赖于默认编码。您可以通过调用getEncoding方法来确定InputStreamReaderOutputStreamWriter使用的编码,如下所示:

InputStreamReader defaultReader = new InputStreamReader(fis);
String defaultEncoding = defaultReader.getEncoding();

下面的示例向您展示如何使用InputStreamReaderOutputStreamWriter类执行字符集转换。此示例的完整源代码在StreamConverter.java中。此程序显示日文字符。在尝试之前,请验证系统上是否已安装适当的字体。如果您使用与版本 1.1 兼容的 JDK 软件,请复制font.properties文件,然后用font.properties.ja文件替换它。

StreamConverter程序将String对象中的一系列 Unicode 字符转换为以 UTF-8 编码的字节的FileOutputStream。执行转换的方法称为writeOutput

static void writeOutput(String str) {
    try {
        FileOutputStream fos = new FileOutputStream("test.txt");
        Writer out = new OutputStreamWriter(fos, "UTF8");
        out.write(str);
        out.close();
    } 
    catch (IOException e) {
        e.printStackTrace();
    }
}

readInput方法从由writeOutput方法创建的文件中读取以 UTF-8 编码的字节。InputStreamReader对象将 UTF-8 编码的字节转换为 Unicode,并以String形式返回结果。readInput方法如下:

static String readInput() {
    StringBuffer buffer = new StringBuffer();
    try {
        FileInputStream fis = new FileInputStream("test.txt");
        InputStreamReader isr = new InputStreamReader(fis, "UTF8");
        Reader in = new BufferedReader(isr);
        int ch;
        while ((ch = in.read()) > -1) {
            buffer.append((char)ch);
        }
        in.close();
        return buffer.toString();
    } 
    catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}

StreamConverter程序的main方法调用writeOutput方法创建一个以 UTF-8 编码的字节文件。readInput方法读取相同的文件,将字节转换回 Unicode。以下是main方法的源代码:

public static void main(String[] args) {
    String jaString = new String("\u65e5\u672c\u8a9e\u6587\u5b57\u5217");
    writeOutput(jaString); 
    String inputString = readInput();
    String displayString = jaString + " " + inputString;
    new ShowString(displayString, "Conversion Demo");
}

原始字符串(jaString)应与新创建的字符串(inputString)完全相同。为了显示这两个字符串相同,程序将它们连接起来,并使用 ShowString 对象显示它们。ShowString 类使用 Graphics.drawString 方法显示字符串。此类的源代码在 ShowString.java 中。当 StreamConverter 程序实例化 ShowString 时,会出现以下窗口。显示的字符重复验证了这两个字符串是相同的:

这是 StreamConverter 程序的屏幕截图

规范化文本

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

规范化是一种过程,通过该过程您可以对文本执行某些转换,使其在以前可能无法协调的方式中协调。比如说,您想要搜索或排序文本,在这种情况下,您需要将该文本规范化以考虑应表示为相同文本的代码点。

什么可以被规范化?当您需要转换带有变音符号的字符,更改所有字母大小写,分解连字,或将半角片假名字符转换为全角字符等时,规范化是适用的。

根据Unicode 标准附录#15,规范化器的 API 支持java.text.Normalizer.Form中定义的以下四种 Unicode 文本规范化形式:

  • 规范化形式 D(NFD):规范分解

  • 规范化形式 C(NFC):规范分解,然后规范组合

  • 规范化形式 KD(NFKD):兼容性分解

  • 规范化形式 KC(NFKC):兼容性分解,然后规范组合

让我们看看拉丁小写字母"o"带分音符号如何通过使用这些规范化形式进行规范化:

原始单词 NFC NFD NFKC NFKD
"schön" "schön" "scho\u0308n" "schön" "scho\u0308n"

您可以注意到在 NFC 和 NFKC 中原始单词保持不变。这是因为在 NFD 和 NFKD 中,复合字符被映射到它们的规范分解形式。但是在 NFC 和 NFKC 中,如果可能的话,组合字符序列被映射到复合字符。对于分音符号,没有复合字符,因此在 NFC 和 NFKC 中保持分解状态。

在稍后表示的代码示例NormSample.java中,您还可以注意到另一个规范化特性。半角和全角片假名字符将具有相同的兼容性分解,因此它们是兼容性等效的。但是,它们不是规范等效的。

要确保您确实需要规范化文本,您可以使用isNormalized方法来确定给定的字符序列是否已规范化。如果此方法返回 false,则意味着您需要规范化此序列,并且应使用normalize方法,该方法根据指定的规范化形式规范化char值。例如,要将文本转换为规范分解形式,您将需要使用以下normalize方法:

normalized_string = Normalizer.normalize(target_chars, Normalizer.Form.NFD);

此外,规范化方法会将重音符号重新排列为正确的规范顺序,因此您无需担心自行重新排列重音符号。

以下示例表示一个应用程序,使您能够选择规范化形式和模板进行规范化:


注意: 如果您看不到 applet 运行,请安装至少 Java SE Development Kit (JDK) 7 版本。


这个 applet 的完整代码在 NormSample.java 中。

使用 JTextComponent 类处理双向文本

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

本节讨论如何使用JTextComponent类处理双向文本。双向文本是包含从左到右和从右到左两个方向运行的文本。双向文本的一个示例是包含从右到左运行的阿拉伯文本(包含从左到右运行的数字)。显示和管理双向文本更加困难;但是JTextComponent会为您处理这些问题。

下面涵盖了以下主题:

  • 确定双向文本的方向性

  • 显示和移动插入符

  • 命中测试

  • 高亮选择

  • 设置组件方向

欲了解更多信息,或者如果您想更好地控制处理这些问题,请参阅处理双向文本在 2D 图形教程中。

确定双向文本的方向性

示例BidiTextComponentDemo.java,基于TextComponentDemo.java,在JTextPane对象中显示双向文本。在大多数情况下,Java 平台可以确定双向 Unicode 文本的方向性:

BidiTextComponentDemo.java

在 JTextComponent 对象中明确指定文本运行方向

您可以指定JTextComponent对象的Document对象的运行方向。例如,以下语句指定JTextPane对象textPane中的文本从右到左运行:

textPane.getDocument().putProperty(
    TextAttribute.RUN_DIRECTION,
    TextAttribute.RUN_DIRECTION_RTL);

或者,您可以根据语言环境指定特定 Swing 组件的组件方向。例如,以下语句指定对象textPane的组件方向基于 ar-SA 语言环境:

Locale arabicSaudiArabia = 
    new Locale.Builder().setLanguage("ar").setRegion("SA").build();

textPane.setComponentOrientation(
    ComponentOrientation.getOrientation(arabicSaudiArabia));

因为阿拉伯语的运行方向是从右到左,所以textPane对象中包含的文本的运行方向也是从右到左。

有关更多信息,请参阅设置组件方向。

显示和移动插入符

在可编辑文本中,插入符用于图形表示当前插入点,即文本中新字符将插入的位置。在BidiTextComponentDemo.java示例中,插入符包含一个小三角形,指向插入字符将显示的方向。

默认情况下,JTextComponent对象创建一个键映射(类型为Keymap),该键映射作为所有JTextComponent实例共享的默认键映射。键映射允许应用程序将按键绑定到操作。默认键映射(用于支持插入符移动的JTextComponent对象)包括将插入符向前和向后移动与左右箭头键绑定,从而支持通过双向文本移动插入符。

点击测试

通常,设备空间中的位置必须转换为文本偏移量。例如,当用户在可选择文本上单击鼠标时,鼠标的位置将被转换为文本偏移量,并用作选择范围的一端。从逻辑上讲,这是定位插入符的逆过程。

您可以将插入符监听器附加到JTextComponent的实例上。插入符监听器使您能够处理插入符事件,这些事件发生在插入符移动或文本组件中的选择更改时。您可以使用addCaretListener方法附加插入符监听器。有关更多信息,请参见如何编写插入符监听器。

高亮选择

一段选定的字符范围在图形上由一个高亮区域表示,该区域是以反色或不同背景颜色显示字形的区域。

JTextComponent对象实现了逻辑高亮。这意味着选定的字符在文本模型中始终是连续的,而高亮区域可以是不连续的。以下是逻辑高亮的示例:

BidiTextComponentDemo:逻辑高亮

设置组件方向

Swing 的布局管理器了解区域设置如何影响用户界面;不需要为每个区域设置创建新的布局。例如,在文本从右到左流动的区域,布局管理器将以相同的方向排列组件。

示例 InternationalizedMortgageCalculator.java 已本地化为英语,美国;英语,英国;法语,法国;法语,加拿大;以及阿拉伯语,沙特阿拉伯。

以下示例使用 en-US 区域设置:

按揭计算器,en-US 区域设置

以下示例使用 ar-SA 区域设置:

按揭计算器,ar-SA 区域设置

请注意,组件的布局与相应区域设置的方向相同:en-US 为从左到右,ar-SA 为从右到左。示例 InternationalizedMortgageCalculator.java 调用方法 applyComponentOrientationgetOrientation 来指定其组件的方向:

private static JFrame frame;

// ...

private static void createAndShowGUI(Locale currentLocale) {

    // Create and set up the window.
    // ...
    // Add contents to the window.
    // ...
    frame.applyComponentOrientation(
        ComponentOrientation.getOrientation(currentLocale));
    // ...
  }

示例 InternationalizedMortgageCalculator.java 需要以下资源文件:

  • resources/Resources.properties

  • resources/Resources_ar.properties

  • resources/Resources_fr.properties

课程:网络资源的国际化

原文:docs.oracle.com/javase/tutorial/i18n/network/index.html

在现代互联网社区中,许多用户不再满足于仅使用 ASCII 符号来识别域名或网络资源。例如,他们希望能够注册一个新的域名,使用阿拉伯语或中文中的本地字符。这就是为什么网络资源的国际化是拓宽万维网视野的基石。

本课程描述了网络域名资源的国际化。

国际化域名

国际化域名

原文:docs.oracle.com/javase/tutorial/i18n/network/idn.html

从历史上看,互联网域名仅包含 ASCII 符号。随着互联网的普及和在全球范围内的采用,支持域名的国际化变得必要,特别是支持包含 Unicode 字符的域名。

应用程序中的国际化域名(IDNA)机制被采用为将 Unicode 字符转换为标准 ASCII 域名的标准,从而保持域名系统的稳定性。该系统执行查找服务,将用户友好的名称转换为网络地址。

国际化域名的示例:

  • http://清华大学.cn

  • http://www.транспорт.com

如果您点击这些链接,您会看到地址栏中表示的 Unicode 域名被替换为 ASCII 字符串。

要在应用程序中实现类似的功能,java.net.IDN类提供了方法,用于在 ASCII 和非 ASCII 格式之间转换域名。

方法 目的
toASCII(String) toASCII(String, flag) 在将 IDN 发送到域名解析系统或写入期望 ASCII 字符的文件(例如 DNS 主文件)之前使用。如果输入字符串不符合RFC 3490,这些方法会抛出IllegalArgumentException异常。
toUnicode(String) toUnicode(String, flag) 在向用户显示名称时使用,例如从 DNS 区域获取的名称。此方法将字符串从 ASCII 兼容编码(ACE)转换为 Unicode 代码点。此方法永远不会失败;在出现错误时,输入字符串保持不变并原样返回。

可选的flag参数指定转换过程的行为。ALLOW_UNASSIGNED标志允许包含 Unicode 3.2 中未分配的代码点。USE_STD3_ASCII_RULES标志确保遵守 STD-3 ASCII 规则。您可以单独使用这些标志或逻辑上合并它们。如果不需要任何标志,请使用该方法的单参数版本。

教程:国际化的服务提供者

原文:docs.oracle.com/javase/tutorial/i18n/serviceproviders/index.html

国际化的服务提供者使得可以插入区域相关的数据和服务。由于可以插入区域相关的数据和服务,第三方能够提供java.textjava.util包中大多数区域敏感类的实现。

服务是一组提供对特定应用程序功能或特性访问的编程接口和类。服务提供者接口(SPI)是服务定义的一组公共接口和抽象类。服务提供者实现了 SPI。服务提供者使您能够创建可扩展的应用程序,可以在不修改其原始代码库的情况下进行扩展。您可以通过新的插件或模块增强其功能。有关服务提供者和可扩展应用程序的更多信息,请参阅创建可扩展应用程序。

您可以使用国际化的服务提供者来提供以下区域敏感类的自定义实现:

相应的 SPI 包含在java.text.spijava.util.spi包中:

java.util.spi java.text.spi

|

|

|

例如,如果您想为新的区域设置提供一个NumberFormat对象,请实现java.text.spi.NumberFormatProvider类并实现以下方法:

  • getCurrencyInstance(Locale locale)

  • getIntegerInstance(Locale locale)

  • getNumberInstance(Locale locale)

  • getPercentInstance(Locale locale)

Locale loc = new Locale("da", "DK");
NumberFormat nf = NumberFormatProvider.getNumberInstance(loc);

这些方法首先检查 Java 运行时环境是否支持请求的区域设置;如果支持,则使用该支持。否则,方法会调用已安装提供者的适当接口的getAvailableLocales方法,以找到支持请求的区域设置的提供者。

要深入了解如何使用国际化服务提供者的示例,请参阅将自定义资源包作为扩展安装。本节展示了如何实现ResourceBundleControlProvider接口,使您能够在不对应用程序源代码进行任何其他更改的情况下使用任何自定义ResourceBundle.Control类。

安装自定义资源包作为扩展

原文:docs.oracle.com/javase/tutorial/i18n/serviceproviders/resourcebundlecontrolprovider.html

自定义资源包加载部分向您展示如何更改资源包的加载方式。这涉及从类ResourceBundle.Control派生一个新类,然后通过调用以下方法检索资源包:

ResourceBundle getBundle(
  String baseName,
  Locale targetLocale,
  ResourceBundle.Control control)

参数control是您对ResourceBundle.Control的实现。

java.util.spi.ResourceBundleControlProvider接口使您能够更改以下方法加载资源包的方式:

ResourceBundle getBundle(
  String baseName,
  Locale targetLocale)

请注意,此版本的ResourceBundle.getBundle方法不需要ResourceBundle.Control类的实例。ResourceBundleControlProvider是一个服务提供者接口(SPI)。SPI 使您能够创建可扩展的应用程序,即可以在不修改其原始代码库的情况下轻松扩展的应用程序。有关更多信息,请参阅创建可扩展应用程序。

要使用 SPI,首先通过实现类似ResourceBundleControlProvider的 SPI 来创建一个服务提供者。当您实现一个 SPI 时,您指定它将如何提供服务。ResourceBundleControlProvider SPI 提供的服务是在应用程序调用方法ResourceBundle.getBundle(String baseName, Locale targetLocale)时获取适当的ResourceBundle.Control实例。您将服务提供者与 Java 扩展机制一起打包为已安装的扩展。运行应用程序时,不需要在类路径中命名您的扩展;运行时环境会找到并加载这些扩展。

已安装的ResourceBundleControlProvider SPI 实现将替换默认的ResourceBundle.Control类(定义了默认的包加载过程)。因此,ResourceBundleControlProvider接口使您可以使用任何自定义的ResourceBundle.Control类,而无需对应用程序的源代码进行任何其他更改。此外,该接口使您能够编写应用程序,而无需引用任何自定义的ResourceBundle.Control类。

RBCPTest.java示例展示了如何实现ResourceBundleControlProvider接口以及如何将其打包为已安装的扩展。这个示例被打包在 zip 文件RBCPTest.zip中,包含以下文件:

  • src

    • java.util.spi.ResourceBundleControlProvider

    • RBCPTest.java

    • rbcp

      • PropertiesResourceBundleControl.java

      • PropertiesResourceBundleControlProvider.java

      • XMLResourceBundleControl.java

      • XMLResourceBundleControlProvider.java

    • resources

      • RBControl.properties

      • RBControl_zh.properties

      • RBControl_zh_CN.properties

      • RBControl_zh_HK.properties

      • RBControl_zh_TW.properties

      • XmlRB.xml

      • XmlRB_ja.xml

  • lib

    • rbcontrolprovider.jar
  • build:包含所有打包在rbcontrolprovider.jar中的文件以及类文件RBCPTest.class

  • build.xml

以下步骤展示了如何重新创建文件RBCPTest.zip的内容,RBCPTest示例的工作原理以及如何运行它:

  1. 创建 ResourceBundle.Control 类的实现。

  2. 实现 ResourceBundleControlProvider 接口。

  3. 在应用程序中调用 ResourceBundle.getBundle 方法。

  4. 通过创建配置文件注册服务提供者。

  5. 将提供者、其所需的类以及配置文件打包到一个 JAR 文件中。

  6. 运行 RBCPTest 程序。

1. 创建 ResourceBundle.Control 类的实现。

RBCPTest.java 示例使用了两种ResourseBundle.Control的实现:

  • PropertiesResourceBundleControlProvider.java:这是在自定义资源包加载中定义的相同ResourceBundle.Control实现。

  • XMLResourceBundleControl.java:这个ResourceBundle.Control实现使用Properties.loadFromXML方法加载基于 XML 的包。

XML 属性文件

如在 使用属性文件支持 ResourceBundle 部分所述,属性文件是简单的文本文件。它们每行包含一个键值对。XML 属性文件与属性文件类似:它们包含键值对,只是具有 XML 结构。以下是 XML 属性文件 XmlRB.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE properties [
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) >
<!ELEMENT entry (#PCDATA) >
<!ATTLIST entry key CDATA #REQUIRED>
]>

<properties>
    <comment>Test data for RBCPTest.java</comment>
    <entry key="type">XML</entry>
</properties>

以下是等效的属性文本文件:

# Test data for RBCPTest.java
type = XML

所有 XML 属性文本文件具有相同的结构:

  • 指定文档类型定义(DTD)的 DOCTYPE 声明:DTD 定义了 XML 文件的结构。注意:在 XML 属性文件中,你可以使用以下 DOCTYPE 声明:

    <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
    
    

    系统 URI (http://java.sun.com/dtd/properties.dtd) 在导出或导入属性时不会被访问;它是一个唯一标识 XML 属性文件的 DTD 的字符串。

  • 根元素 <properties>:此元素包含所有其他元素。

  • 任意数量的 <comment> 元素:用于注释。

  • 任意数量的 <entry> 元素:使用属性 key 指定键;在 <entry> 标签之间指定键的值。

查看 Properties 类以获取有关 XML 属性文件的更多信息。

2. 实现 ResourceBundleControlProvider 接口。

这个接口包含一个方法,ResourceBundle.Control getControl(String baseName) 方法。参数 baseName 是资源包的名称。在 getBundle 方法的定义中,指定应返回的 ResourceBundle.Control 实例,给定资源包的名称。

RBCPTest 示例包含 ResourceBundleControlProvider 接口的两个实现,PropertiesResourceBundleControlProvider.javaXMLResourceBundleControlProvider.java。如果资源包的基本名称以 resources.RBControl 开头,则方法 PropertiesResourceBundleControlProvider.getBundle 返回 PropertiesResourceBundleControl 的实例(在此示例中,所有资源文件都包含在 resources 包中):


package rbcp;

import java.util.ResourceBundle;
import java.util.spi.ResourceBundleControlProvider;

public class PropertiesResourceBundleControlProvider
    implements ResourceBundleControlProvider {
    static final ResourceBundle.Control PROPERTIESCONTROL =
        new PropertiesResourceBundleControl();

    public ResourceBundle.Control getControl(String baseName) {
        System.out.println("Class: " + getClass().getName() + ".getControl");
        System.out.println("    called for " + baseName);

        // Throws a NPE if baseName is null.
        if (baseName.startsWith("resources.RBControl")) {
            System.out.println("    returns " + PROPERTIESCONTROL);
            return PROPERTIESCONTROL;
        }
        System.out.println("    returns null");
        System.out.println();
        return null;
    }
}

类似地,如果资源包的基本名称以 resources.Xml 开头,则方法 XMLResourceBundleControlProvider.getControl 返回 XMLResourceBundleControl 的实例。

注意:你可以创建一个实现 ResourceBundleControlProvider 接口的实现,根据基本名称返回 PropertiesResourceBundleControlXMLResourceBundleControl 的实例。

3. 在你的应用程序中,调用方法 ResourceBundle.getBundle。

RBCPTest使用方法ResourceBundle.getBundle检索资源包:


import java.io.*;
import java.net.*;
import java.util.*;

public class RBCPTest {
    public static void main(String[] args) {
        ResourceBundle rb = ResourceBundle.getBundle(
            "resources.XmlRB", Locale.ROOT);
        String type = rb.getString("type");
        System.out.println("Root locale. Key, type: " + type);
        System.out.println();

        rb = ResourceBundle.getBundle("resources.XmlRB", Locale.JAPAN);
        type = rb.getString("type");
        System.out.println("Japan locale. Key, type: " + type);
        System.out.println();

        test(Locale.CHINA);
        test(new Locale("zh", "HK"));
        test(Locale.TAIWAN);
        test(Locale.CANADA);
    }

    private static void test(Locale locale) {
        ResourceBundle rb = ResourceBundle.getBundle(
            "resources.RBControl", locale);
        System.out.println("locale: " + locale);
        System.out.println("    region: " + rb.getString("region"));
        System.out.println("    language: " + rb.getString("language"));
        System.out.println();
    }
}

请注意,此类中没有ResourceBundle.ControlResourceBundleControlProvider的实现。因为ResourceBundleControlProvider接口使用 Java 扩展机制,运行时环境会找到并加载这些实现。但是,使用 Java 扩展机制安装的ResourceBundleControlProvider实现和其他服务提供程序是使用ServiceLoader类加载的。使用此类意味着您必须使用配置文件注册服务提供程序,下一步将对此进行描述。

4. 通过创建配置文件注册服务提供程序。

配置文件的名称是提供程序实现的接口或类的完全限定名称。配置文件包含您的提供程序的完全限定类名。文件java.util.spi.ResourceBundleControlProvider包含PropertiesResourceBundleControlProviderXMLResourceBundleControlProvider的完全限定名称,每行一个名称:

rbcp.XMLResourceBundleControlProvider
rbcp.PropertiesResourceBundleControlProvider

5. 将提供程序、其所需类和配置文件打包到 JAR 文件中。

编译源文件。从包含build.xml文件的目录中,运行以下命令:

javac -d build src/java.* src/rbcp/*.java

此命令将编译src目录中包含的源文件,并将类文件放在build目录中。在 Windows 上,请确保使用反斜杠(\)来分隔目录和文件名。

创建一个 JAR 文件,其中包含编译的类文件、资源文件和配置文件,目录结构如下:

  • META-INF

    • services

      • java.util.spi.ResourceBundleControlProvider
  • rbcp

    • PropertiesResourceBundleControl.class

    • PropertiesResourceBundleControlProvider.class

    • XMLResourceBundleControl.class

    • XMLResourceBundleControlProvider.class

  • 资源

    • RBControl.properties

    • RBControl_zh.properties

    • RBControl_zh_CN.properties

    • RBControl_zh_HK.properties

    • RBControl_zh_TW.properties

    • XmlRB.xml

    • XmlRB_ja.xml

注意,配置文件java.util.spi.ResourceBundleControlProvider必须打包在目录/META-INF/services中。此示例将这些文件打包在lib目录中的 JAR 文件rbcontrolprovider.jar中。

查看在 JAR 文件中打包程序以获取有关创建 JAR 文件的更多信息。

或者,下载并安装Apache Ant,这是一个可以帮助您自动化构建过程的工具,例如编译 Java 文件和创建 JAR 文件。确保 Apache Ant 可执行文件在您的PATH环境变量中,以便您可以从任何目录运行它。安装 Apache Ant 后,请按照以下步骤操作:

  1. 编辑文件build.xml,将${JAVAC}更改为您的 Java 编译器javac的完整路径名,并将${JAVA}更改为您的 Java 运行时可执行文件java的完整路径名。

  2. 从包含文件build.xml的相同目录运行以下命令:

    ant jar
    

    这个命令编译 Java 源文件并将它们与所需的资源和配置文件打包到lib目录中的 JAR 文件rbcontrolprovider.jar中。

6. 运行 RBCPTest 程序。

在命令提示符下,从包含build.xml文件的目录运行以下命令:

java -Djava.ext.dirs=lib -cp build RBCPTest

此命令假定以下情况:

  • 包含 RBCPTest 示例编译代码的 JAR 文件位于lib目录中。

  • 编译后的类RBCPTest.class位于build目录中。

或者,使用 Apache Ant,并从包含build.xml文件的目录运行以下命令:

ant run

当您安装 Java 扩展时,通常将扩展的 JAR 文件放在 JRE 的lib/ext目录中。但是,此命令使用系统属性java.ext.dirs指定包含 Java 扩展的目录。

RBCPTest程序首先尝试使用基本名称为resources.XmlRB和区域设置为Locale.ROOTLocal.JAPAN的资源包。程序检索这些资源包的输出类似于以下内容:

Class: rbcp.XMLResourceBundleControlProvider.getControl
    called for resources.XmlRB
    returns rbcp.XMLResourceBundleControl@16c1857
Root locale. Key, type: XML

Class: rbcp.XMLResourceBundleControlProvider.getControl
    called for resources.XmlRB
    returns rbcp.XMLResourceBundleControl@16c1857
Japan locale. Key, type: Value from Japan locale

该程序成功获取XMLResourceBundleControl的实例并访问属性文件XmlRB.xmlXmlRB_ja.xml

RBCPTest程序尝试检索资源包时,它调用配置文件java.util.spi.ResourceBundleControlProvider中定义的所有类。例如,当程序检索基本名称为resources.RBControl和区域设置为Locale.CHINA的资源包时,它打印以下输出:

Class: rbcp.XMLResourceBundleControlProvider.getControl
    called for resources.RBControl
    returns null

Class: rbcp.PropertiesResourceBundleControlProvider.getControl
    called for resources.RBControl
    returns rbcp.PropertiesResourceBundleControl@1ad2911
locale: zh_CN
    region: China
    language: Simplified Chinese

教程:2D 图形

原文:docs.oracle.com/javase/tutorial/2d/index.html

本教程向你介绍了 Java 2D™ API,并展示了如何在 Java 程序中显示和打印 2D 图形。本教程适用于希望丰富其对 Java 2D API 知识的开发人员,以及计算机图形初学者。几乎每个部分都包含相关示例来说明特定功能。Java 2D API 可以轻松执行以下任务:

  • 绘制线条、矩形和任何其他几何形状。

  • 用纯色、渐变和纹理填充这些形状。

  • 使用选项绘制文本,对字体和渲染过程进行精细控制。

  • 绘制图像,可选择应用滤镜操作。

  • 在上述任何渲染操作期间应用诸如合成和变换等操作。

本章还解释了较不熟悉的概念,如合成。

这幅图代表了不同的图形功能使用 2D 图形 API 显示复杂图表

这幅图代表了滤镜操作的使用使用图像滤镜操作

本章描述了在屏幕和离屏图像、表面和打印机设备上绘制的概念。本教程涵盖了 Java 2D API 的最常见用法,并简要描述了一些更高级功能。

介绍了关键的 Java 2D 概念,并描述了 Java 2D 渲染模型。这节课比本教程的其他课程更具概念性,它让你深入了解基本概念和类描述。

使用一个开发的示例向你展示如何获取 Graphics 对象并将其用于常见的图形渲染任务。

教你如何使用 API 绘制图形基元和任意形状,以及如何应用花哨的描边和填充样式。

展示了如何有效地使用文本 API,包括如何创建具有所需属性的Font对象,测量文本,并确定系统中可用字体的名称。

解释了如何创建 BufferedImage 对象,执行图像滤镜操作,并在图像上绘制。

教你如何将 2D 图形渲染到打印机,打印复杂文档,并使用打印服务。

解释了如何执行变换,裁剪绘图区域,合成重叠图形,指定渲染偏好,并控制渲染质量。

课程:Java 2D API 概念概述

原文:docs.oracle.com/javase/tutorial/2d/overview/index.html

Java 2D API 通过对抽象窗口工具包(AWT)的扩展,为 Java 程序提供了二维图形、文本和图像功能。这个全面的渲染包支持线条艺术、文本和图像,提供了一个灵活、功能齐全的框架,用于开发更丰富的用户界面、复杂的绘图程序和图像编辑器。Java 2D 对象存在于一个称为用户坐标空间或用户空间的平面上。当对象在屏幕或打印机上呈现时,用户空间坐标被转换为设备空间坐标。以下链接对开始学习 Java 2D API 很有帮助:

Java 2D API 提供以下功能:

  • 用于显示设备和打印机的统一渲染模型

  • 一系列几何基元,如曲线、矩形和椭圆,以及一种渲染几乎任何几何形状的机制

  • 用于在形状、文本和图像上执行命中检测的机制

  • 一个合成模型,提供对重叠对象如何呈现的控制

  • 增强的颜色支持,有助于颜色管理

  • 支持打印复杂文档

  • 通过使用渲染提示控制渲染质量

这些主题在以下章节中讨论:

  • Java 2D 渲染

  • 几何基元

  • 文本

  • 图像

  • 打印

posted @ 2024-04-12 14:58  绝不原创的飞龙  阅读(54)  评论(0)    收藏  举报