一个Java字符串中到底有多少个字符?

作者:鸟窝
来源:https://colobu.com/
 

一个Java字符串中到底有多少个字符?

文章插图
 
依照JAVA的文档 ,  Java中的字符内部是以UTF-16编码方式表示的 , 最小值是 \u0000 (0),最大值是\uffff(65535) ,  也就是一个字符以2个字节来表示 , 难道Java最多只能表示 65535个字符?
char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).
from The Java™ Tutorials
首先 , 让我们先看个例子:
public class Main { public static void main(String[] args) { // 中文常见字 String s = "你好"; System.out.println("1. string length =" + s.length()); System.out.println("1. string bytes length =" + s.getBytes().length); System.out.println("1. string char length =" + s.toCharArray().length); System.out.println(); // emojis s = ""; System.out.println("2. string length =" + s.length()); System.out.println("2. string bytes length =" + s.getBytes().length); System.out.println("2. string char length =" + s.toCharArray().length); System.out.println(); // 中文生僻字 s = "妹"; System.out.println("3. string length =" + s.length()); System.out.println("3. string bytes length =" + s.getBytes().length); System.out.println("3. string char length =" + s.toCharArray().length); System.out.println(); }}运行这个程序 , 你觉得输出结果是什么?
输出结果:
1. string length =21. string bytes length =61. string char length =22. string length =42. string bytes length =82. string char length =43. string length =33. string bytes length =73. string char length =3我们知道 ,  String.getBytes()如果不指定编码格式 , Java会使用操作系统的编码格式得到字节数组 , 在我的macOS中 , 默认使用UTF-8作为字符编码(locale命令可以查看操作系统的编码) , 所以在我的机器运行 , String.getBytes()会返回UTF-8编码的字节数组 。
  • String.length返回Unicode code units的长度 。
  • String.toCharArray返回字符数组 。
我们设置的字符串都是两个unicode字符 , 输出结果:
  • 普通的中文字:字符串的长度是2 , 每个中文字按UTF-8编码是三个字节 , 字符数组的长度看起来也没问题
  • emojis字符:我们设置了两个emojis字符 , 男女头像 。结果字符串的长度是4, UTF-8编码8个字节 , 字符数组的长度是4
  • 生僻的中文字:我们设置了两个中文字 , 其中一个是生僻的中文字 。结果字符串的长度是3 ,  UTF-8编码7个字节 , 字符数组的长度是3
看起来字符串的字符数和我们预期的有点不一样 , 我们的字符串只有两个unicode字符, 可是输出结果有时候是2 , 有时候是3 ,  有时候是4 , 为什么呢?
这还得从Java的历史说起 。
Java最初设计的Charactor用两个字节来表示unicode字符 , 这没有问题 ,  因为最初unicode中的字符还比较少 ,  Java 1.1之前采用Unicode version 1.1.5, JDK 1.1中支持Unicode 2.0, JDK 1.1.7支持Unicode 2.1, Java SE 1.4 支持 Unicode 3.0, Java SE 5.0开始支持Unicode 4.0 。
直到Unicode 3.0, Java用两个字节来表示unicode字符还没有问题 , 因为Unicode 3.0最多49,259个字符 ,  两个字节可以表示65,535个字符 , 还足够容的下所有的uicode3.0字符 。
但是Unicode 4.0(事实上自Unicode 3.1), 字符集进行很大的扩充 , 已经达到了96,447个字符 , Unicode 11.0已经包含137,374个字符 。
在Unicode中 , 为每一个字符对应一个编码点(一个整数) , 用 U+紧跟着十六进制数表示 。所有字符按照使用上的频繁度划分为 17 个平面(编号为 0-16) , 即基本的多语言平面和增补平面 。基本的多语言平面(英文为 Basic Multilingual Plane , 简称 BMP)又称平面 0 , 收集了使用最广泛的字符 。
这样一来 , Java的Charactor的两个字节的设计 , 已经不足以容纳所有的Unicode 4的字符 ,  所以可能需要4个字节才能表示扩展字符 , 所以现在的Charactor代表的已经不再是一个字符 (代码点 code point), 而是一个代码单元(code unit) 。