Character

字符,涉及到计算机的组成原理,编码等知识,这里记录下理解的概念。

计算机常识

  1. 计算机只能识别 0 和 1

  2. 0 和 1,能表示 2 进制数

  3. 2 进制数能转为人类可直观阅读的 10进制数

  4. 人为规定每个 10 进制数字代表什么字符,就成为了编码(映射)。

  5. 规定不同,导致每个数字代表的字符不同,就形成了不同的编码规范,例如GBK(中国规范)、Unicode(国标)、其他国家也有自己字符的编码。

  6. unicode是为了统一所有的字符而产生的。java默认采用的编码规范。

为什么会出现乱码

编码和解码方式不一致,就会导致乱码。比如通过 GBK 进行编码,数字 1 可能代表一个汉字,编码后为 2 进制数,但是如果用其他编码进行解析,例如 unicode,这个 2 进制数,代表的可能是另外一个字符,显示出来就不是最初的哪个汉字,当然如果没有这个数字对应的字符,就会出现乱码。

Unicode 基本知识

  1. Unicode又称为统一码、万国码、单一码,是国际组织制定的旨在容纳全球所有字符的编码方案,包括字符集、编码方案等,它为每种语言中的每个字符设定了统一且唯一的二进制编码,以满足跨语言、跨平台的要求。

  2. Unicode 字符集被分成多个区,Unicode 将“区”称为平面。Unicode 字符集被划分为 17 个平面(即,17 个区,编号为 0-16 )

  3. 每个平面有 216 = 65536 个代码点,每个代码点都可以代表一个字符。

  4. 并不是每个码点就一定对应有一个字符,因为,目前 Unicode 字符集中有很多码点都还未被使用。

  5. 17个平面又分为以下两类平面。基本多文种平面 ( Basic Multilingual Plane,BMP,或称为基本多语言平面、基本平面、第零平面、平面 0 ),辅助平面(或称为增补平面),每类平面都有各自划分含义,这里不深究。

  6. 增补字符使用两个 char 型变量来表示

  7. 2 类平面不做深入研究,只需要知道,javaCharacter 类中有很多方法来检测码点属于哪个平面。

  8. 码点空间,从 0 到上限值之间的范围称为码点空间,例如 Unicode 基本平面的空间为 0 ~ 65535

  9. 代码点(Code Point,简称码点)和码点值,codePoint 在 Character 类中常出现。码点是指码点空间中的一个位置,该位置所代表的数值就是码点值

Character.java 源码解析

(1)很多静态常量

都是一些 Unicode 规范的类别分类,知道即可。

(2)很多判断方法

多数用于判断码点的类型、属于哪个平面、是否是某一个类型。

(3)一些常规的方法

比较,toString、hashCode等

(4)重要的内部类

UnicodeBlock、UnicodeScript

UnicodeBlock 主要是一个静态 map 集合,键是字符串,代表哪一类字符,值是 UnicodeBlock。例如中日韩的字符, CJK_SYMBOLS_AND_PUNCTUATION

UnicodeScript:是一个枚举,Unicode script 分类。

(5)重要的字段

blockStarts,这是一个 int 数组。每一个数字都是某一类字符的起始数字,上一类字符的结束数字。例如,下标为 0 的数字是 0x0000,下标为 1 的数字是 0x0080,则 0000..007F 代表 Basic Latin,依次类推,每个相邻的下标数字范围代表一类字符。

(6)字符缓存

CharacterCache,静态内部类,缓存了0到127,共128个字符。

(7)2 分查找法

public static UnicodeBlock of(int codePoint) {
    if (!isValidCodePoint(codePoint)) {
        throw new IllegalArgumentException();
    }

    int top, bottom, current;
    bottom = 0;
    top = blockStarts.length;
    current = top/2;

    // invariant: top > current >= bottom && codePoint >= unicodeBlockStarts[bottom]
    while (top - bottom > 1) {
        if (codePoint >= blockStarts[current]) {
            bottom = current;
        } else {
            top = current;
        }
        current = (top + bottom) / 2;
    }
    return blocks[current];
}

扩展

CharacterData0E、CharacterData00、CharacterData01、CharacterData02、CharacterDataLatin1、CharacterDataPrivateUse、CharacterDataUndefined

这些类都是对字符进行定义操作的一些类,实际用处不大,了解即可。

一些方法解析

    // 最大值,其二进制是 16 个 1.
    public static final char MAX_VALUE = '\uFFFF';

    // 使用移位运算,小于 max_value 的数字,右移 16 为都应该为 0
    public static boolean isBmpCodePoint(int codePoint) {
        return codePoint >>> 16 == 0;
        // Optimized form of:
        //     codePoint >= MIN_VALUE && codePoint <= MAX_VALUE
        // We consistently use logical shift (>>>) to facilitate
        // additional runtime optimizations.
    }

    // 同理,判读一个数字是否在一个范围内,都进行右移 16 位,然后比较大小
    public static boolean isValidCodePoint(int codePoint) {
        // Optimized form of:
        //     codePoint >= MIN_CODE_POINT && codePoint <= MAX_CODE_POINT
        int plane = codePoint >>> 16;
        return plane < ((MAX_CODE_POINT + 1) >>> 16);
    }

如何获取字符其 codepoint ?

char anyChar = '你';
int number = (int) anyChar;
System.out.println(number);