杰瑞科技汇

Java字符串长度如何计算?

length() 方法 - 获取字符数量(char 数量)

这是最常用、最基础的方法,它返回的是 String 对象内部表示的 char 数组的长度。

Java字符串长度如何计算?-图1
(图片来源网络,侵删)

定义

public int length()

作用

返回 String 对象中的 16-bit char 数量

示例

String str1 = "Hello";
System.out.println(str1.length()); // 输出 5
String str2 = "你好,世界";
System.out.println(str2.length()); // 输出 6 (每个中文字符在Java中通常占一个char)

重要提醒: 在 Java 中,char 类型是一个 16-bit 的无符号 Unicode 字符,这意味着 length() 方法返回的是 char 的数量,而不是用户感知的“字符”数量。


codePointCount() 方法 - 获取真正的字符数量(Grapheme Cluster)

现代 Unicode 字符集非常复杂,一个用户看到的“字符”(称为 Grapheme Cluster)可能由一个或多个 char 组成。length() 方法在这种情况下会给出错误的结果。

codePointCount() 方法就是为了解决这个问题而生的。

Java字符串长度如何计算?-图2
(图片来源网络,侵删)

定义

public int codePointCount(int beginIndex, int endIndex)

作用

返回从 beginIndexendIndex - 1 范围内,Unicode 代码点的数量,一个代码点可能对应一个 char,也可能对应一对 char(称为 代理对 Surrogate Pair)。

代理对

当 Unicode 字码超出 char 的表示范围(即大于 0xFFFF)时,Java 使用两个 char 来表示它,这被称为代理对,常见的例子包括:

  • Emoji 表情符号 (😂, 👨‍👩‍👧‍👦)
  • 某些特殊字符或字母 (如 𝄞, 𝒻)

示例

// 示例 1: Emoji 笑脸
String emoji = "😂";
System.out.println("length() 返回: " + emoji.length()); // 输出 2 (这是一个代理对)
System.out.println("codePointCount() 返回: " + emoji.codePointCount(0, emoji.length())); // 输出 1 (这才是用户感知的字符数量)
// 示例 2: 复合家庭 Emoji
String familyEmoji = "👨‍👩‍👧‍👦"; // 一个字符,由多个代码点组成
System.out.println("length() 返回: " + familyEmoji.length()); // 输出 11
System.out.println("codePointCount() 返回: " + familyEmoji.codePointCount(0, familyEmoji.length())); // 输出 1
// 示例 3: 普通字符串
String normal = "Java";
System.out.println("length() 返回: " + normal.length()); // 输出 4
System.out.println("codePointCount() 返回: " + normal.codePointCount(0, normal.length())); // 输出 4

如果你的应用需要处理国际化文本、Emoji 或任何可能包含代理对的字符,并且你需要计算用户能看到的“字符”数量,codePointCount() 是正确且可靠的选择。


charAt()codePointAt() - 获取字符

与长度相对应,获取字符也有两个方法。

charAt(int index)

返回指定索引处的 char 值,如果该索引是一个代理对的第二个 char,它只会返回那个孤立的 char,这通常不是你想要的结果。

String emoji = "😂";
char c1 = emoji.charAt(0); // 获取代理对的高位 surrogate
char c2 = emoji.charAt(1); // 获取代理对的低位 surrogate
System.out.println(c1); // 输出类似 '?' 的符号,实际是 '\uD83D'
System.out.println(c2); // 输出类似 '?' 的符号,实际是 '\uDE02'
//单独看 c1 和 c2 都没有意义

codePointAt(int index)

返回指定索引处的 Unicode 代码点,如果该索引是一个代理对的起始位置,它会正确地解析并返回整个代码点的整数值。

String emoji = "😂";
int codePoint = emoji.codePointAt(0); // 从索引0开始,获取完整的代码点
System.out.println(codePoint); // 输出 128514 (这是 '😂' 的 Unicode 码位)

总结与最佳实践

方法 返回值 适用场景 备注
length() int (16-bit char 的数量) 索引遍历字符串。
2. 需要知道底层 char 数组大小时。
3. 确定字符串在内存中占用的基本空间。
最快,但不能准确反映用户感知的字符数,对 Emoji 等无效。
codePointCount() int (Unicode 代码点的数量) 需要计算用户能看到的“字符”数量时(如限制输入长度)。
2. 处理国际化文本、Emoji 时。
3. 需要精确处理所有 Unicode 字符时。
最准确,但计算成本比 length() 稍高。
charAt() char 在确定字符不包含代理对的情况下,进行简单的字符遍历。 对于代理对,会返回无意义的单个 char
codePointAt() int (Unicode 代码点) 在遍历字符串时,正确处理包含代理对的字符。 推荐用于需要正确解析所有字符的遍历场景。

如何选择?

  1. 如果你只是想遍历字符串的每一个 char(进行某种简单的、不考虑语义的转换),并且不关心代理对,使用 length()charAt() 是可以的。

    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        // 处理 c...
    }
  2. 如果你需要正确处理所有 Unicode 字符,包括 Emoji,并且想得到用户感知的字符数量或正确遍历,你应该使用 codePointCount()codePointAt()

    int count = str.codePointCount(0, str.length());
    System.out.println("用户感知的字符数: " + count);
    // 正确遍历
    int[] codePoints = str.codePoints().toArray(); // 最简单的方式
    for (int cp : codePoints) {
        // 处理代码点 cp...
    }
    // 或者手动遍历
    int i = 0;
    while (i < str.length()) {
        int codePoint = str.codePointAt(i);
        // 处理代码点 codePoint...
        i += Character.charCount(codePoint); // 关键:根据代码点大小移动索引
    }
  3. 如果你需要限制用户输入的长度(比如发微博最多140个字),绝对不能使用 length(),否则用户输入一个 Emoji 就会占用两个字符,导致实际体验到的字符数远少于限制,你应该使用 codePointCount()

    String userInput = "今天天气真好😊";
    int maxLength = 10;
    // 错误的做法
    if (userInput.length() > maxLength) {
        System.out.println("输入过长!"); // "今天天气真好😊" 长度为 10,但用户感知是 9 个字符
    }
    // 正确的做法
    if (userInput.codePointCount(0, userInput.length()) > maxLength) {
        System.out.println("输入过长!"); // 会正确判断为超限
    }

希望这个详细的解释能帮助你完全理解 Java String 的字符长度问题!

分享:
扫描分享到社交APP
上一篇
下一篇