文章目录
- 一、字符编码
- 1、Unicode
- 2、utf-8、utf-16和utf-32区别
- 二、码点和代码单元
- 1、码点和代码单元
- 2、实战理解
- 3、遍历字符串
一、字符编码
1、Unicode
Unicode(万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。
Unicode 为世界上所有字符都分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF (十六进制),有 110 多万,每个字符都有一个唯一的 Unicode 编号,这个编号一般写成 16 进制,在前面加上 U+。例如:“马”的 Unicode 是U+9A6C。Unicode 就相当于一张表,建立了字符与编号之间的联系。
所以Unicode**本身只规定了每个字符的数字编号是多少,并没有规定这个编号如何存储。**因此才有了多种字符编码的存储方案,比较常见就是国际通用字符编码utf-8。
2、utf-8、utf-16和utf-32区别
UTF-32是一种定长编码,使用1个32bit的码元,其值与Unicode编码值相等
UTF-16也是一种变长编码,对于一个Unicode字符被编码成1至2个码元,每个码元为16位。在基本多语言平面内的码位UTF-16编码使用1个码元且其值与Unicode是相等的(不需要转换),但在辅助平面内的码位在UTF-16中被编码为一对16bit的码元(即32bit,4字节)
UTF-8是一种变长编码,对于一个Unicode的字符被编码成1至4个字节。Unicode编码与UTF-8的编码的对应关系:
Unicode编码 | UTF-8编码(二进制) |
U+0000 – U+007F | 0xxxxxxx |
U+0080 – U+07FF | 110xxxxx 10xxxxxx |
U+0800 – U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U+10000 – U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
一个字节的uft8表示的unicode 码范围为(0 ~0x7F)
两个字节长度的uft8 表示的unicode码范围为(0x80 ~ 0x07FF)
三个字节长度的uft8 表示的unicode码范围为(0x0800 ~ 0xFFFF)
四个字节长度的uft8 表示的unicode码范围为( 0x10000 ~ 0x10FFFF)
二、码点和代码单元
1、码点和代码单元
Java中char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元,最常用的Unicode字符使用一个代码单元就可以表示,而辅助字符则需要一对代码单元表示。
通俗理解是有些Unicode字符可以用一个char值表示,表示一个码点,一个代码单元;但另外的Unicode字符则需要两个char值来表示,即辅助字符需要用两个char值,但表示一个码点,两个代码单元。
2、实战理解
字符串中如果有一些非常规字符的话,使用charAt会导致结果不正确
public class Test {
public static void main(String[] args) {
String test="🍷Hello";
System.out.println(test);
System.out.println(test.length());//得到的是代码单元的数量7, 🍷占两个代码单元
System.out.println(test.codePointCount(0,test.length()));//得到的是码点的数量6
System.out.println((int)test.charAt(2));//返回第三个的代码单元:得到H的int型
int index=test.offsetByCodePoints(0,1);//得到第二个码点位置
int cp=test.codePointAt(index);//得到该位置码点int型表示
System.out.println(cp);
}
}
3、遍历字符串
对于无非常规字符的字符串,我们可以使用charAt来遍历(代码单元遍历);而对于非常规字符的字符串,我们就只能遍历码点来实现
public class Test {
public static void main(String[] args) {
// 也可以实现反向遍历
// if(Character.isSurrogate(test.charAt((i)))) i--;
String test = "🍷Hello";
for (int i = 0; i < test.length(); ) {
int cp = test.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;
System.out.print((char) cp);
}
}
}
第二种方法
public class Test {
public static void main(String[] args) {
String test="🍷Hello";
int [] codePoints=test.codePoints().toArray();
for(int i=0;i<codePoints.length;i++){
System.out.println((char)codePoints[i]);
}
//将码点数组转换成字符串
String str=new String(codePoints,0,codePoints.length);
//🍷Hello
System.out.println(str);
}
}