百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

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

sinye56 2024-09-27 21:15 14 浏览 0 评论

依照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 =2
1. string bytes length =6
1. string char length =2
2. string length =4
2. string bytes length =8
2. string char length =4
3. string length =3
3. string bytes length =7
3. 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)。

  • Code Point: 代码点,一个字符的数字表示。一个字符集一般可以用一张或多张由多个行和多个列所构成的二维表来表示。二维表中行与列交叉的点称之为代码点,每个码点分配一个唯一的编号数字,称之为码点值或码点编号,除开某些特殊区域(比如代理区、专用区)的非字符代码点和保留代码点,每个代码点唯一对应于一个字符。从U+0000 到 U+10FFFF。
  • Code Unit:代码单元,是指一个已编码的文本中具有最短的比特组合的单元。对于 UTF-8 来说,代码单元是 8 比特长;对于 UTF-16 来说,代码单元是 16 比特长。换一种说法就是 UTF-8 的是以一个字节为最小单位的,UTF-16 是以两个字节为最小单位的。

Java的字符在内部以UTF-16编码方式来表示,String.length返回的是Code Unit的长度,而不再是Unicode中字符的长度。对于传统的BMP平面的代码点,String.length和我们传统理解的字符的数量是一致的,对于扩展的字符,String.length可能是我们理解的字符长度的两倍。


有可能你会问, 对于一个UTF-16编码的扩展字符,它以4个字节来表示,那么前两个字节会不会和BMP平面冲突,导致程序不知道它是扩展字符还是BMP平面的字符?

其实是不会的, 幸运的是, 在BMP平面中, U+D800到U+DFFF之间的码位是永久保留不映射到Unicode字符,UTF-16就利用保留下来的0xD800-0xDFFF区块的码位来对辅助平面的字符的码位进行编码。

UTF-16编码中,辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,需要20位来表示。第一个整数(两个字节,称为前导代理)要容纳上述20位的前10位,第二个整数(称为后尾代理)容纳上述20位的后10位。

前导代理的值的范围是0xD800到0xDBFF,后尾代理的0xDC00~0xDFFF。可以看到前导代理和后尾代理的范围都落在了BMP平面中不用来映射的码位,所以不会产生冲突,而且前导代理和后尾代理也没有重合。

这样我们得到两个字节的,就可以直接判断它是否是BMP平面的字符,还是扩展字符中的前导代理还是后尾代码。

国外的有些用户用emojis字符做自己的昵称,导致有些系统不能正确的显示出来,这是因为这些系统粗暴的使用Charactor来表示,在显示的时候截断的时候有时候可能不是在正确的代码点上进行截断。

我们在进行字符串截取的时候,比如String.substring有可能会踩到一些坑,尤其经常使用的emojis字符。

自 Java 1.5 java.lang.String就提供了Code Point方法, 用来获取完整的Unicode字符和Unicode字符数量:

  • public int codePointAt(int index)
  • public int codePointBefore(int index)
  • public int codePointCount(int beginIndex, int endIndex)

注意这些方法中的index使用的是code unit值。

参考文档

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Character.html
http://www.oracle.com/us/technologies/java/supplementary-142654.html
https://stackoverflow.com/questions/2533097/java-unicode-encoding
https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html
https://zh.wikipedia.org/wiki/Unicode
https://codeahoy.com/2016/05/08/the-char-type-in-java-is-broken/
https://zh.wikipedia.org/wiki/UTF-16
https://wiki.sei.cmu.edu/confluence/display/java/STR50-J.+Use+the+appropriate+method+for+counting+characters+in+a+string
http://stn.audible.com/abcs-of-unicode/#common-unicode-mistakes-in-java-apps

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: