详解Character中的相关code-point方法
- 缘由
- Unicode和Code-point的关系
- API
- Charatcer.toCodePoint
- Character.isValidCodePoint
- Character.codePointCount
- Character.isHighSurrogate\Character.isLowSurrogate
- Character.codePointAt
- Character.codePointBefore
- Character.highSurrogate\Character.lowSurrogate
- Character.isSurrogate
- Character.isSurrogatePair
- Character.isSupplementaryCodePoint
- Character.offsetByCodePoints
- Character.isBmpCodePoint
- 总结
缘由
在java中有许多针对code-point的相关方法,由于工作需要,对部分常用的code-point方法进行记录下。
Unicode和Code-point的关系
“一个普通字符是一位,一个emoji表情是两位。Mysql应该用utf8mb4存储emoji,而不应该用utf8”这是大多数人真正意义上去了解到unicode的开端,关于unicode的一些基础知识在这里就不普及了。为了方便,下面的特殊unicode的展示都用emoji作为示例(毕竟这是大多数人所认知的双位字符)。
下面有一段代码
代码块1
String ch = "a";
System.out.println(ch.length());
很显然,输出的结果是1,因为a是一位;又有一段代码
代码块2
String emoji = "😠";
System.out.println(emoji.length());
这里的输出是2,但其实,这个emoji表情对于code-point来说的话其实是一个(应该用codePointCount来统计,后面API会说)。
那么现在有一个问题,有时候我们需要循环处理字符串中的内容,如果遇到这种情况会怎样?来看一段代码
代码块3
String ch = "abcd";
for (int i = 0; i < ch.length(); i++) {
System.out.println(ch.charAt(i));
}
很显然,会分别打印出来a、b、c、d,那如果打印我们代码块2中的emoji时会发生什么事情,来看下代码
代码块4
String emoji = "😠";
for (int i = 0; i < emoji.length(); i++) {
System.out.println(emoji.charAt(i));
}
不看答案,想一下会打印出什么。
自己去试一下的话会发现,打印出了两行问号,并不是一些人所认定的两行unicode代码(这个emoji的实际代码是\uD83D\uDE20)。
这样的情况对于我们去遍历一个字符串是不友好的。
API
Charatcer.toCodePoint
toCodePoint方法是将一个unicode的高位和低位进行结合后拼接输出,例如😠对应的unicode的是\uD83D\uDE20,可以调用codePoint对其进行拼接。
System.out.println( Character.toCodePoint('\uD83D','\uDE20'));
打印结果是128544,对应这个unicode字符。
Character.isValidCodePoint
isValidCodePoint用于判断当前输入是否是一个标准的codepoint,判断的标准和unicode的定义保持一致,是否位于0x000000~0X10FFFF之间
Character.codePointCount
计算数组中的codepoint数量,正如上方代码块2下方的解释一样,一个emoji应当算一个,而“你😠好”的codepoint的数量应当是3而不是4。
Character.isHighSurrogate\Character.isLowSurrogate
isHighSurrogate和isLowSurrogate是用于判断高位和地位的方法,例如😠对应的unicode的是\uD83D\uDE20,我们分别看下。
代码块5
System.out.println( Character.isHighSurrogate('\uD83D'));
System.out.println( Character.isHighSurrogate('\uDE20'));
System.out.println( Character.isLowSurrogate('\uD83D'));
System.out.println( Character.isLowSurrogate('\uDE20'));
结果分别是true\false\false\true,很显然,\uD83D是高位,\uDE20是低位。注意,如果是单字符,非双字节unicode,无论是isHighSurrogate还是isLowSurrogate都将返回false。
Character.codePointAt
codePointAt方法有多个实现,但终归是返回每一个codepoint的unicode代码,以刚刚代码块4进行举例.
代码块6
char[] emoji = "ab😠cd".toCharArray();
System.out.println(Character.codePointAt(emoji, 1));
System.out.println(Character.codePointAt(emoji, 2));
System.out.println(Character.codePointAt(emoji, 3));
这个地方打印出来的是结果如下。
98
128544
56864
注意,代码块6的打印结果中,第2行是结合了两个unicode得出来的结果,而第三方只是这个unicode的低位。
Character.codePointBefore
codePointBefore和codePointAt逻辑一样,但是在判断时不是当前的index,而是index之前的结果,例如传入的index是2,则查询的范围是0-1,注意,此处也会进行高低位的判断。
Character.highSurrogate\Character.lowSurrogate
highSurrogate和lowSurrogate是分别打印出一个双位unicode的高位和低位的数字结果,例如:
System.out.println( Character.toCodePoint(Character.highSurrogate(128544),Character.lowSurrogate(128544)));
打印结果自然是128544,还原了高位和低位
Character.isSurrogate
isSurrogate用大白话说,就是判断是否是合理的双位字符,合理的UTF-16字符最小是\u005CuD800例如如下代码:
代码块7
char[] emoji = "😠".toCharArray();
System.out.println( Character.isSurrogate(emoji[0]));
System.out.println( Character.isSurrogate(emoji[1]));
System.out.println( Character.isSurrogate('a'));
结果自然前两行是true,最后一行是false。
Character.isSurrogatePair
isSurrogatePair是用于判断两个字符是否是双位字符的一对(高位+低位),例如如下代码
代码块8
char[] emoji = "😠".toCharArray();
System.out.println( Character.isSurrogatePair(emoji[0],emoji[1]));
System.out.println( Character.isSurrogatePair(emoji[0],emoji[0]));
第一行的结果是true,第二行的结果是false,很显然。
Character.isSupplementaryCodePoint
isSupplementaryCodePoint的作用是判断是否是一个双位字符,和isSurrogate的区别是,isSupplementaryCodePoint只要是两位就是对的,而isSurrogate必须是合理的UTF-16字符。
Character.offsetByCodePoints
方法有两种实现,第一种是在一个字符串中,输入index和offset,求以codepoint角度下[index,index+offset]区间的最后一个codepoint后的对于array的索引,例如:
代码块9
System.out.println( Character.offsetByCodePoints("ab😠cd",1,3));
System.out.println( Character.offsetByCodePoints("abcde",1,3));
结果是5,4
"ab😠cd"的[1,4]范围内其实指的就是"b😠c"的最后一位"c"后的的array的index,则是5。
Character.isBmpCodePoint
这里涉及到一个概念,Bmp指的是Basic Multilingual Plane,如下的概念来自于百度百科
基本多文种平面,BMP(Basic Multilingual Plane),或称第零平面(Plane 0),是Unicode中的一个编码区段。编码从U+0000至U+FFFF。 Unicode 基本多文种平面的示意图。每个写着数字的格子代表256个码点。
其实就是判断输入字符是否在U+0000至U+FFFF范围内。
总结
总结了一下常用的Character中的常用对于unicode和surrogate的方法,对于codepoint也做了部分的解释,需要注意的地方是,有些API需要输入的是unicode的int值,而有些需要输入char,还有就是要深入理解"双位"字符的真正含义,BMP和UTF-16的区别。