点击上方“朱小厮的博客”,选择“设为星标”

后台回复”加群“加入公众号专属技术群

来源:rrd.me/fpv95

依照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();// emojiss = "????????";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值。

想知道更多?描下面的二维码关注我

【限时推广1】

极客时间双十二全场优惠,特定申请了一个15元(满40减15)优惠口令:NIUBISIDA(上次的SIDANIUBI用掉了...),有效期截止日期本月月底。

【精彩推荐】

  • 入侵你Linux服务器的一万种玩法...

  • 955 不加班的公司名单:955.WLB

  • 阿里巴巴的独立环境是如何实现的

朕已阅 

一个字符串中到底能有多少个字符? 我竟然算错了!相关推荐

  1. 面试官问:一个Java字符串中到底能有多少个字符?

    作者 | 鸟窝 来源 | urlify.cn/qYNR3q 依照Java的文档, Java中的字符内部是以UTF-16编码方式表示的,最小值是 \u0000 (0),最大值是\uffff(65535) ...

  2. 面试题:寻找一个字符串中出现次数最多的字符以及出现的次数

    要求编写代码实现:寻找一个字符串中出现次数最多的字符以及出现的次数. 解法一:用删除法实现 (挺巧妙的一种) public class FindTheMostAppearChar {public st ...

  3. 【算法】大搜车——找出一个字符串中出现次数最多的字符,如果有多个出现次数相同的字符,那就找出最先出现的那个字符

    题目描述:找出一个字符串中出现次数最多的字符,如果有多个出现次数相同的字符,那就找出最先出现的那个字符 思考: 我的第一反应是分三步: 遍历字符串,统计每个字符出现的次数 找出最大的次数 找出最大次数 ...

  4. 找一个字符串中出现次数最多的字符

    第一种: str = 'asdasddsfdsfadsfdghdadsdfdgdasd'str = str.split('');console.log(str);var newStr = {};// ...

  5. 判断一个字符串中出现次数最多的字符,统计这个次数

    var str = 'abaasdffggghhjjkkgfddsssss3444343';// 1.将字符串转换成数组var newArr = str.split("");// ...

  6. 删除一个字符串中指定位置上的字符

    题目描述 输入一串字符,输入要删除元素的位置,输出删除后的字符串 输入 一行字符串 输出 删除后的字符 样例输入 abcde 3 样例输出 abde #include<stdio.h> i ...

  7. 判断一个字符串中出现次数最多的字符

    var str='assshghrhhdaa';var obj={};for(var i=0,l=str.length,k;i<l;i++){ k=str.charAt(i); if(obj[k ...

  8. 删除字符串中出现次数最少的字符

    在公交车上看一博客实现删除字符串中出现次数最少的字符,认为里面使用数组来作为hash很好,所以我就自己实现一遍. 要求:实现删除一个字符串中出现次数最少的字符.输出删除后的字符,要求安装原来顺序输出. ...

  9. 统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数

    统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数.(不考虑其他字符) 1.需求: 统计一个字符串中大写字母字符,小写字母字符,数字字符出现的次数.(不考虑其他字符) 举例: " ...

最新文章

  1. 一文探讨 RPC 框架中的服务线程隔离
  2. Luogu T9376 区间GCD
  3. mysql数据库的数据类型转换_MySQL 和Server 2000 数据库中数据类型的转换
  4. 线性代数拾遗(五):矩阵变换的应用
  5. 快手海外管理层人事动荡,两大市场负责人已离职 疑因业务发展不佳
  6. Java -- AWT 画图,图像处理
  7. PYTHON1.day06
  8. 该拒绝MSN Messager了
  9. HDU1274 展开字符串【文本处理】
  10. 分布式系统负载均衡策略分析与研究
  11. 安卓简单实现百度地图
  12. 线性代数 (二): 证明 A^T 乘以 A 为半正定矩阵
  13. 计算机应用情话,2018最新版情话大全浪漫情话 好听感人的情话
  14. 《工业控制系统信息安全防护指南》实施建议 (上)
  15. 这三年,一路走来,劈荆斩棘 – Vol 2
  16. CorelDraw插件开发-VBA-常用功能-二维码创建-cdr插件
  17. python1234出栈_Python语句序列“nums=set([1,2,2,3,3,3,4]);print(len(nums))”的运行结果是()。 (2.0分)_学小易找答案...
  18. python大学生社团管理系统
  19. [RK3288][Android6.0] 调试笔记 --- Activity多次调用finish()引起的Warning
  20. 影响网站收录量有哪些因素

热门文章

  1. dice系数什么意思_轮胎上的数字和字母是什么意思 轮胎上的红点黄点是什么意思...
  2. redis(12)--事件,客户端,服务器
  3. python调用winrar解压_批量文件解压缩脚本(Python3.5 + WinRAR)
  4. linux防火墙阻断目的,基于Linux防火墙的内部邮件监控与阻断系统
  5. 在操作系统理论中,什么是饿死
  6. 关于提高代码复用性的几个知识点的回顾
  7. 零元学Expression Blend 4 - Chapter 38 看如何使用Clip修出想要的完美曲线(下)
  8. RHEL6入门系列之十一,内/外部命令、重定向、管道
  9. 运维监控之Nagios实战(五)Nagios QL3.11
  10. 线程安全退出 VS PostMessage,SendMessage的区别