使用 UnicodeBlock 打印国际象棋符号?

Print chess symbols using UnicodeBlock?

随着 jdk12,出现了国际象棋符号 (source):

Unicode 11.0.0 introduced the following new features that are now included in JDK 12

[...] 4 blocks for the following existing scripts:

  • Georgian Extended

  • Mayan Numerals

  • ndic Siyaq Numbers

  • Chess Symbols

考虑到这一点,我尝试使用以下代码打印这些字符,以测试功能并稍后在一个小国际象棋游戏中使用它们:

Character.UnicodeBlock block = Character.UnicodeBlock.CHESS_SYMBOLS;
for (int i = 0; i < 1114112; i++) {
    char unicode = (char) i;
    if(Character.UnicodeBlock.of(unicode) == block) {
        System.out.println(unicode);
    }
}

但是,它没有打印任何东西。例如,如果我将 CHESS_SYMBOLS 替换为 ARABIC,代码就可以工作。我有 java 12.0.1.

问题:为什么上面的代码没有打印任何东西?

一些国际象棋符号字符存在于 Miscellaneous Symbols 块中,但您专门检查不同块中的 16 位 char 值。国际象棋符号块包含具有 16 位值的 个字符;它从 U+1FA00 开始,到 U+1FA6F 结束。

通过强制转换为 char,您将把所有高于 U+FFFF 的值修剪到它们的最低 16 位;例如,如果 i0x1fa60,将其转换为 char 将使它成为 0xfa60,这会阻止您的块检查成功。

要使您的代码正常工作,您需要停止假设所有代码点都是 16 位值。你可以通过改变这个来做到这一点:

char unicode = (char) i;

对此:

int unicode = i;

不幸的是,Character.UnicodeBlock 没有方法来判断块中代码点的开始值和结束值。在 Unicode 11 中,国际象棋符号块从 U+1FA00 到 U+1FA6D。

Java 使用 UTF-16 和 surrogate pairs to represent characters over U+10000。在这种情况下,代码点 U+1FA00 将表示为两个 char 值:U+D83E(高代理)和 U+DE60(低代理)。

您应该使用 Character.toChars() 来正确打印始终为 int:

的代码点
Character.UnicodeBlock block = Character.UnicodeBlock.CHESS_SYMBOLS;
for (int i = 0; i < 1114112; i++) {
    if (Character.UnicodeBlock.of(i).equals(block)) {
        System.out.println(Character.toChars(i));
    }
}