String.codePointCount() 返回 Unicode code point 数量,准确统计包括 emoji 组合序列在内的所有合法码点,如 "Hello ? ??" 返回 11;而 length() 返回 UTF-16 编码单元数(为 13),二者本质不同。
java 中 string.length() 返回的是 utf-16 编码单元(code unit)个数,而很多 emoji(如 ?、??、?)由多个 utf-16 单元组成(即代理对 surrogate pair 或 emoji 序列),导致 length() 把一个视觉上的“字符”算成 2 个甚至更多。要获得符合人类直觉的「真实字符数」(即 unicode code point 数量),应使用 string.codepointcount()。
理解 codePointCount() 的作用
String.codePointCount(int beginIndex, int endIndex) 统计指定子串范围内 Unicode code point 的数量。每个 emoji 符号(无论是否由代理对或组合序列构成)在 Unicode 层面都对应一个或多个 code point;该方法会正确识别并合并代理对,把 0xD83D 0xDE00(?)当作 1 个 code point,而非 2 个 char。
- 调用
str.codePointCount(0, str.length())即可统计整串文本的真实字符数 - 它自动跳过代理对的低/高位拆分,不依赖正则或手动解析
- 对普通 ASCII、中文、基础 emoji、ZJW+修饰符组合(如 ??)、家庭序列(????)均有效
对比 length() 和 codePointCount() 的典型差异
以字符串 "Hello ? ??" 为例:
-
"Hello ? ??".length()→ 返回 13(H-e-l-l-o-空格-?[2]-空格-?[2]-[1]-?[2] = 5+1+2+1+2+1+2 = 13) -
"Hello ? ??".codePointCount(0, str.length())→ 返回 9("Hello"5 + 空格1 + ?1 + 空格1 + ??1 = 9)
关键点:?? 是一个 emoji 组合序列(woman + ZWJ + laptop),共 4 个 UTF-16 单元,但只算作 1 个 Unicode 标量值(U+1F469 U+200D U+1F4BB),codePointCount() 仍将其识别为 1 个 code point —— 因为 ZWJ(U+200D)本身就是一个合法的 Unicode code point,整个序列是多个 code point 的组合,不是单个 code point;但 codePointCount() 会如实统计其中每一个独立的 code point(即 ?、、? 共 3 个),所以实际结果是 11,不是 9。更正如下:
字符串 "Hello ? ??" 的 code point 分解为:H e l l o [U+0020] [U+1F30D] [U+0020] [U+1F469] [U+200D] [U+1F4BB] → 共 11 个 code point。
因此 codePointCount(0, len) 返回 11,这才是它真正的、符合 Unicode 标准的计数方式。
注意:它不「智能合并」emoji 序列,只是准确计数所有合法的 Unicode code point(包括 ZWJ、变体选择符 VS16、肤色修饰符等)。若需按「用户感知的字形」计数(例如把 ???? 当作 1 个),需额外使用 Unicode 文本分割(Grapheme Cluster Boundary),Java 15+ 可用 java.text.BreakIterator.getCharacterInstance() 或第三方库如 icu4j。
正确使用的代码示例
推荐封装为工具方法,避免每次重复写范围参数:
<font color="#888"><pre class="brush:php;toolbar:false;">public static int visualLength(String s) {<br> if (s == null) return 0;<br> return s.codePointCount(0, s.length());<br>}
测试验证:
<font color="#888"><pre class="brush:php;toolbar:false;">System.out.println(visualLength("A")); // 1<br>System.out.println(visualLength("??")); // 3(U+1F469 + U+200D + U+1F4BB)<br>System.out.println(visualLength("??")); // 2(U+1F44D + U+1F3FB)<br>System.out.println(visualLength("Hello ?!")); // 9
注意事项与边界情况
- 该方法不会过滤控制字符或不可见符(如 ZWSP
\u200B、LRM\u200E),它们也是合法 code point,会被计入 - 若字符串含非法代理对(如孤立高位或低位 surrogates),
codePointCount()仍会将其视为两个独立的 BMP 字符(即各算 1 个 code point),不会抛异常,但结果可能不符合预期 —— 建议入库前做 Unicode 规范化(Normalizer.normalize(s, Form.NFC)) - 性能良好,内部基于遍历 char 数组,时间复杂度 O(n),无正则开销










