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教程。