Java 字素
最后修改于 2025 年 5 月 24 日
在本文中,我们将使用 Java 中的字素。 字素是任何给定语言的书写系统中最小的单位。
字素
字素是任何给定语言的书写系统中最小的基本单位。 它可能由单个字符或多个组合元素组成,例如重音符号、变音符号或连字,形成视觉上不同的符号。 虽然字素本身可能并不总是传达含义,但它在表示语言结构方面起着至关重要的作用。
从历史上看,术语“字符”用于指原始 ASCII 表中的单个符号,该表仅支持有限的字符集。 随着数字文本表示的发展,引入了 Unicode 标准来适应书面语言的巨大多样性,从而能够表示复杂的脚本、表情符号和多字符字素。
Unicode
Unicode 是一种全球公认的标准,可确保在不同的书写系统中一致地编码、表示和处理文本。 它的开发旨在统一字符表示,从而在各种语言、脚本和专用符号(如数学符号和表情符号)中实现无缝文本处理。
在 Java 中,字符串是一个 Unicode 字符序列,充当结构化数据类型,将文本存储为一系列值,通常以字节表示。 Java 内部使用 UTF-16 编码,它支持直接表示大多数字符,同时使用代理对来扩展对超出基本多文种平面 (BMP) 的 Unicode 字符的支持。
除了 ASCII 字符集之外,使用术语“字素”而不是“字符”更合适。 Java 提供了 BreakIterator 类,该类允许迭代字符串中的文本元素(字素)。 文本元素可能由基本字符、代理对或组合字符序列组成,它们共同形成单个显示的符号。
代码点 表示分配给 Unicode 字符的数字标识符。 例如,拉丁字母 Q 对应于 U+0051 代码点,而西里尔小写字母 ж 由 U+0436 表示。 Unicode 为每个字符分配唯一的代码点,确保在各种系统中的文本处理具有普遍兼容性。
字素簇 由一个或多个代码点组成,这些代码点在视觉上组合形成单个图形单元。 例如,印地语音节 ते 由两个代码点组成:U+0924 代表 त,U+0947 代表 े。 正确处理字素簇对于准确呈现复杂脚本至关重要。
字符串的实际存储表示由字节组成,这些字节对每个代码点进行编码。 根据 Unicode 编码方案(例如 UTF-8、UTF-16 或 UTF-32),每个代码点可能需要不同数量的字节才能在内存或存储中完全表示。
Unicode 表
下面是 Unicode 表的一小部分,显示了代码点、字符及其名称
| 代码点 | 字符 | 名称 |
|---|---|---|
| U+0041 | A | 拉丁大写字母 A |
| U+0061 | a | 拉丁小写字母 A |
| U+03B1 | α | 希腊小写字母 ALPHA |
| U+0416 | Ж | 西里尔大写字母 ZHE |
| U+05D0 | א | 希伯来语字母 ALEF |
| U+0924 | त | 天城文字母 TA |
| U+1F600 | 😀 | 咧嘴笑脸 |
代理对
Java 采用 UTF-16 编码方案 来存储 Unicode 文本,使用 16 位(双字节)代码单元来表示每个字符。 此方案支持 基本多文种平面 (BMP) 中的所有字符,范围从 U+0000 到 U+FFFF,其中包括广泛使用的脚本,如拉丁语、希腊语、西里尔语、阿拉伯语、希伯来语、汉语、日语、韩语,以及符号和标点符号。
对于 BMP 之外的 Unicode 字符,范围从 U+10000 到 U+10FFFF,Java 使用 代理对 来扩展 UTF-16 编码功能,从而能够表示这些附加字符。
代理对 是一对 16 位代码单元,它们共同表示 BMP 之外的单个 Unicode 字符。 第一个单元称为高位代理,后跟低位代理。 这种机制使 Java 能够处理扩展字符,例如表情符号、历史脚本和各种领域中使用的专用符号。
Unicode 还支持 组合字符序列,其中基本字符由一个或多个组合字符(例如变音符号)修改。 当与形成单个视觉单元的其他代码点组合时,代理对可以表示独立字符或成为更大的字素簇的一部分。
| 代码点 | 字符 | 高位代理 | 低位代理 | 名称 |
|---|---|---|---|---|
| U+1F600 | 😀 | D83D | DE00 | 咧嘴笑脸 |
| U+1F680 | 🚀 | D83D | DE80 | 火箭 |
| U+1F4A9 | 💩 | D83D | DCA9 | 一堆便便 |
| U+1F60D | 😍 | D83D | DE0D | 带爱心的笑脸 |
| U+1F47B | 👻 | D83D | DC7B | 鬼魂 |
在上表中,代码点 U+1F600、U+1F680、U+1F4A9、U+1F60D 和 U+1F47B 表示为代理对。 高位代理是第一个代码单元 (D83D),低位代理是第二个代码单元 (DE00、DE80、DCA9、DE0D、DC7B)。 这些对允许表示基本多文种平面 (BMP) 之外的字符,其中包括许多表情符号和其他特殊字符。
Java 字素示例
在以下示例中,我们将使用字素。
void main() {
System.out.println("The Hindi word Namaste");
String word = "नमस्ते";
System.out.println(word);
System.out.println();
// code points
System.out.println("Code points:");
for (int i = 0; i < word.length(); i += Character.charCount(word.codePointAt(i))) {
int cp = word.codePointAt(i);
System.out.printf("U+%04X %s%n", cp, new String(Character.toChars(cp)));
}
System.out.println();
// bytes
System.out.println("Bytes:");
byte[] bytes = word.getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
System.out.printf("%d ", b & 0xFF); // Convert signed byte to unsigned
}
System.out.println("\n");
// graphemes
System.out.println("Graphemes:");
BreakIterator graphemeIter = BreakIterator.getCharacterInstance();
graphemeIter.setText(word);
int count = 0;
int start = graphemeIter.first();
while (start != BreakIterator.DONE) {
int end = graphemeIter.next();
if (end == BreakIterator.DONE) {
break;
}
String grapheme = word.substring(start, end);
System.out.println(grapheme);
count++;
start = end;
}
System.out.printf("the word has %d graphemes%n", count);
}
该示例定义了一个变量,其中包含一个印地语单词 namaste。 我们打印该单词,打印其代码点、字节,并打印和计数字素的数量。
String word = "नमस्ते"; System.out.println(word);
我们有印地语单词 namaste; 我们将其打印到控制台。
System.out.println("Code points:");
for (int i = 0; i < word.length(); i += Character.charCount(word.codePointAt(i))) {
int cp = word.codePointAt(i);
System.out.printf("U+%04X %s%n", cp, new String(Character.toChars(cp)));
}
我们打印单词的代码点。 length 方法确定字符串中 UTF-16 字符的数量。 Character.charCount 方法用于确定表示代码点所需的字符值的数量。 如果是代理对,我们需要更多字节来表示一个字素。
使用 word.codePointAt 方法,我们获取指定索引处的 Unicode 代码点。 Character.toChars 方法将代码点转换为 char 数组,我们将其转换为字符串以打印字素。
System.out.println("Bytes:");
byte[] bytes = word.getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
System.out.printf("%d ", b & 0xFF); // Convert signed byte to unsigned
}
我们打印存储在磁盘上的单词的实际字节。 我们使用带有 StandardCharsets.UTF_8 的 getBytes 方法来获取底层字节数组。
System.out.println("Graphemes:");
BreakIterator graphemeIter = BreakIterator.getCharacterInstance();
graphemeIter.setText(word);
int count = 0;
int start = graphemeIter.first();
while (start != BreakIterator.DONE) {
int end = graphemeIter.next();
if (end == BreakIterator.DONE) {
break;
}
String grapheme = word.substring(start, end);
System.out.println(grapheme);
count++;
start = end;
}
我们打印单词的字素并对其进行计数。 BreakIterator.getCharacterInstance 用于枚举单词的字素。 substring 方法提取起始索引和结束索引之间的字素。
$ java Main.java The Hindi word Namaste नमस्ते Code points: U+0928 न U+092E म U+0938 स U+094D ् U+0924 त U+0947 े Bytes: 224 164 168 224 164 174 224 164 184 224 165 141 224 164 164 224 165 135 Graphemes: न म स्ते the word has 3 graphemes
输出显示了单词、其代码点、字节和字素。 字素作为单独的行打印,并且总字素数显示在末尾。 在这种情况下,单词 नमस्ते 由四个字素组成:न、म、स 和 ते。 字素 ते 是基本字符 त 和组合字符 े 的组合,它们共同形成单个字素。
但是,在这种情况下,Java 错误地计算了字素。
来源
用于处理 Unicode 的有用网站:www.fileformat.info 和 www.utf8-chartable.de。
在本文中,我们使用了 Unicode 字符串的字素、代码点、字节和代理对。
作者
列出所有Java教程。