Java代码单元数量与码点数量

Posted 刘润森!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java代码单元数量与码点数量相关的知识,希望对你有一定的参考价值。

前言

介绍一下 byte[] 和 String 相互转换的使用案例。
版本约定

  • JDK 版本:1.8.0_231
  • Java SE API Documentation:https://docs.oracle.com/javase/8/docs/api/

正文

Java 与 Unicode

Java 语言设计之初就认识到统一字符集(Unicode)的重要性,并积极拥抱了问世不久的 Unicode 标准。比如,Java 基本数据类型中 char 最初描述的就是 UCS-2 编码中的代码单元。与其他使用 8 比特字符的语言相比,这是Java 的主要优势。

但尴尬的是,随着更多字符的引入,尤其是汉语、韩语和日文中的表意文字的引入,使得 Unicode 远远超出 16 比特编码的范围。现在,Unicode 需要 21 个比特,表示范围从 0x0 ~ 0x10FFFF。当单元固定长度 16 位的 UCS-2 到达容量上限不能支持更多的 Unicode 字符的时候,Unicode 协会放弃了 UCS-2。取而代之的是 16 位的变长编码 UTF-16。

在 Unicode 从 16 比特向 21 比特过渡时期,Java 语言深受其苦。其中,Java 5.0 版本既要支持 Unicode 4.0 同时又要保证向后兼容性,所以 Java 开始使用 UTF-16 作为其内部编码方式,同时引入码点和代码单元两个概念,在上面有提到这两个概念。

在 Java 中,char 类型描述了 UTF-16 编码中的一个代码单元。

码点和代码单元操作

Java 字符串是由 char 值序列组成。char 数据类型是一个采用 UTF-16 编码表示 Unicode 码点的代码单元。大多数的常用 Unicode 字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。 接下来演示 String 类中码点和代码单元的操作,帮助我们更好的理解这两个概念。
代码单元数量与码点数量

String 类中的 length 方法将返回采用 UTF-16 编码表示的给定字符串所需要的代码单元数量。即它返回的是代码单元的数量,并不是码点的数量,当字符串中存在一个辅助字符时,返回的数量将不会是我们看到的字符个数。

比如下面的例子:

public static void main(String[] args) 
    String name = "james";
    System.out.println(name + " length: " + name.length());
    String message = "𝕆";
    System.out.println(message + " length: " + message.length());

运行程序,输出:

james length: 5
𝕆 length: 2

要想得到实际的长度,即码点数量,调用 codePointCount 方法。

public static void main(String[] args) 
    String name = "james";
    System.out.println(name + " length: " + name.codePointCount(0, name.length()));
    String message = "𝕆";
    System.out.println(message + " length: " + message.codePointCount(0, message.length()));

运行程序,输出:

james length: 5
𝕆 length: 1

位置 n 的代码单元与位置 n 的码点

调用 s.charAt(n) 将返回位置 n 的代码单元,n 介于 0 ~ s.length()-1 之间。

public static void main(String[] args) 
    String name = "james";
    System.out.println(name + " 返回位置 0 的代码单元: " + name.charAt(0));
    String message = "𝕆";
    System.out.println(message + " 返回位置 0 的代码单元: " + message.charAt(0));

运行程序,输出:

james 返回位置 0 的代码单元: j
𝕆 返回位置 0 的代码单元: ?

辅助字符需要 2 个代码单元才可以表示,所以通过 charAt 获得的可能不是某个字符,而是辅助字符的一个代码单元。比如上面的 message.charAt(0) 返回的是 𝕆 的第一个代码单元。为了避免这个问题,不要使用 char 类型,它太底层了。

要想获取字符串的第 i 个码点,应该使用下列语句。

public static void main(String[] args) 
    String name = "james";
    int nameIndex0 = name.offsetByCodePoints(0, 0);
    System.out.println(name + " 返回位置 0 的码点: " + name.codePointAt(nameIndex0));
    String message = "𝕆";
    int messageIndex0 = message.offsetByCodePoints(0, 0);
    System.out.println(message + " 返回位置 0 的码点: " + message.codePointAt(messageIndex0));

运行程序,输出:

james 返回位置 0 的码点: 106
𝕆 返回位置 0 的码点: 120134

判断是否是辅助字符

  • Character 类提供方法 isSupplementaryCodePoint(int codePoint) 用于判断传入的码点是否是辅助字符。
  • Character 类还提供方法 isSurrogate(char ch) 用于判断传入的代码单元是否是辅助字符的一部分。

以上是关于Java代码单元数量与码点数量的主要内容,如果未能解决你的问题,请参考以下文章

Java代码单元数量与码点数量

3.6.6 码点与代码单元

Java中代码点和代码单元是啥?怎么理解?举例说明下,它们之间有啥联系有啥区别?为啥代码点

在Java编码中,如何减少bug数量

你所不知道的charCodeAt与codePointAt(了解js字符串的码元与码点)

Java中的代码点和代码单元(转)