教程:处理文本
几乎所有带有用户界面的程序都会操作文本。在国际市场上,您的程序显示的文本必须符合世界各地语言的规则。Java 编程语言提供了许多类来帮助您以与区域设置无关的方式处理文本。
检查字符属性
本节解释了如何使用Character
比较方法来检查所有主要语言的字符属性。
比较字符串
在本节中,您将学习如何使用Collator
类执行与区域设置无关的字符串比较。
检测文本边界
本节展示了BreakIterator
类如何检测字符、单词、句子和行边界。
转换非 Unicode 文本
世界各地的不同计算机系统使用各种编码方案存储文本。本节描述了帮助您在 Unicode 和其他编码之间转换文本的类。
规范化器的 API
本节解释了如何使用规范化器的 API 来应用不同的规范化形式转换文本。
使用 JTextComponent 类处理双向文本
本节讨论了如何处理双向文本,即包含从左到右和从右到左两个方向的文本。
检查字符属性
您可以根据字符的属性对字符进行分类。例如,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 文档。以下示例显示了如何使用getType
和Character
类别常量。这些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 无关的比较
排序规则定义字符串的排序顺序。这些规则因区域而异,因为各种自然语言对单词的排序方式不同。您可以使用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
定制整理规则
前一节讨论了如何使用区域设置的预定义规则来比较字符串。这些整理规则确定字符串的排序顺序。如果预定义的整理规则不符合您的需求,您可以设计自己的规则并将其分配给RuleBasedCollator
对象。
定制的整理规则包含在传递给RuleBasedCollator
构造函数的String
对象中。这里是一个简单的例子:
String simpleRule = "< a < b < c < d"; RuleBasedCollator simpleCollator = new RuleBasedCollator(simpleRule);
对于前面示例中的simpleCollator
对象,a
小于b
,b
小于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
提高排序性能
对长字符串列表进行排序通常是耗时的。如果您的排序算法重复比较字符串,可以通过使用CollationKey
类加快该过程。
一个CollationKey
对象代表给定String
和Collator
的排序键。比较两个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
对象数组进行排序。为了检索每个CollationKey
的String
表示形式,程序在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
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 – 列出了与
Character
、String
和相关类相关的 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。
以下表格显示了几个字符的代码点值:
如前所述,范围在 U+10000 到 U+10FFFF 之间的字符称为补充字符。从 U+0000 到 U+FFFF 的字符集有时被称为基本多语言平面(BMP)。
更多术语可以在更多信息页面上列出的Unicode 术语词汇表中找到。
补充字符作为代理
原文:
docs.oracle.com/javase/tutorial/i18n/text/supplementaryChars.html
为了支持补充字符而不改变char
原始数据类型并导致与之前的 Java 程序不兼容,补充字符被定义为一对称为代理的代码点值。 第一个代码点来自U+D800
到U+DBFF
的高代理范围,第二个代码点来自U+DC00
到U+DFFF
的低代理范围。 例如,Deseret 字符 LONG I,U+10400
,是用这对代理值定义的:U+D801
和U+DC00
。
字符和字符串 API
原文:
docs.oracle.com/javase/tutorial/i18n/text/characterClass.html
Character
类封装了 char
数据类型。对于 J2SE 发布的第 5 版,许多方法被添加到 Character
类中以支持补充字符。这个 API 分为两类:将 char
和代码点值之间进行转换的方法以及验证或映射代码点的方法。
本节描述了 Character
类中可用方法的一个子集。有关可用 API 的完整列表,请参阅 Character
类规范。
转换方法和 Character 类
以下表格包括了 Character
类中最有用的转换方法或便于转换的方法。codePointAt
和 codePointBefore
方法包含在此列表中,因为文本通常以序列的形式出现,比如一个 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) |
返回CharSequence 或char 数组中的 Unicode 代码点数。 |
isLowerCase(int) isUpperCase(int) |
如果指定的 Unicode 代码点是小写或大写字符,则返回 true。 |
isDefined(int) |
如果指定的 Unicode 代码点在 Unicode 标准中定义,则返回 true。 |
isJavaIdentifierStart(char) isJavaIdentifierStart(int) |
如果指定的字符或 Unicode 代码点可作为 Java 标识符中的第一个字符,则返回 true。 |
isLetterOrDigit(int)
| 如果指定的 Unicode 代码点是字母、数字或字母数字,则返回 true。 |
getDirectionality(int) |
返回给定 Unicode 代码点的 Unicode 方向性属性。 |
Character.UnicodeBlock.of(int codePoint) |
返回表示包含给定 Unicode 代码点的 Unicode 块的对象,如果代码点不是已定义块的成员,则返回null 。 |
字符串类中的方法
String
、StringBuffer
和 StringBuilder
类也有构造函数和方法,可以处理补充字符。以下表格列出了一些常用方法。要查看可用 API 的完整列表,请参阅 String
、StringBuffer
和 StringBuilder
类的 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 代码点数。 |
| String.offsetByCodePoints(int index, int codePointOffset)
StringBuffer.offsetByCodePoints(int index, int codePointOffset)
StringBuilder.offsetByCodePoints(int index, int codePointOffset)
| 返回给定索引偏移给定代码点数量后的索引。 |
Java 中文官方教程 2022 版(三十)(2)https://developer.aliyun.com/article/1487960