charAt()和length()都是经常使用到的命令,这两个命令在遇到某些特殊字符的时候会出现一些问题。比如:
public class Demo{
public static void main(String args[]){
String s = "👲";
char c = s.charAt(0);
System.out.println(s.length());
System.out.println(c);
}
}
可以发现这个特殊字符(或者叫表情符号会好一些)的长度居然是2,而且我把他作为字符输出的时候居然输出了一个问号,这是为什么?
原因是length()方法是跟据UTF-16的代码单元数量来计算字符串的长度的,在UTF-16中常用字符用一个代码单元就可以表示,而有些字符则需要两个代码单元表示,比如"👲",所以我们才会看到这个字符串竟然占据了2个长度。
那为什么变量c会输出问号呢?,没错,charAt()方法也是根据代码单元来获取字符的,变量c只获取了👲的第一个代码单元所以编译器并不能识别。
这在遍历字符串的时候可能会出现很严重的问题:
public class Demo {
public static void main(String[] args) {
String str = "👲🎁🔜😀😁🤣";
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
System.out.println(c);
}
}
}
另外,当我把println换成print再输出后却得到了正确的字符。
可以看出当两个特定的代码单元组合后编译器会把它当作一个字符看待。
解决方法
最好的解决办法就是不使用charAt()和length()方法(除非你能保证接收的字符串都是常用的字符)。
首先说length()方法取字符数量错误的问题,要解决这个问题先要了解一个术语,叫“码点”,有人也叫“代码点”,码点指的是一个编码表中的某个字符所对应的代码值,所以码点的数量才是字符串中字符的数量。
- 方法一:要想得到码点数量可以用codePointCount(0,str.length())方法,然后再用offsetByCodePoints(0,i)获取码点的索引值index,根据index用codePointAt(index)方法得出码点值,最后用Character.toString(cp)取出字符(返回的其实是字符串,要想真正的取出char类型的值是不可能的)。
- 方法二:用codePoints()生成一个流,再用toArray()使其返回一个码点值的数组,然后根据这个数组使用Character.toString(codePoints(i))方法循环输出。
附完整代码:
public class Demo {
public static void main(String[] args) {
String str = "👲🎁🔜😀😁🤣";
//方法一
for (int i = 0; i < str.codePointCount(0,str.length()); i++) {
int index = str.offsetByCodePoints(0,i);
int cp = str.codePointAt(index);
System.out.println(Character.toString(cp));
}
System.out.println("-------------------------------------");
//方法二
int[] codePoints = str.codePoints().toArray();
for (int i = 0; i < codePoints.length; i++) {
System.out.println(Character.toString(codePoints[i]));
}
}
}
本人java菜鸟,水平有限,若有错误恳请留言指正。