文章目录

  • 一、字符编码
  • 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);
    }
}