如何从变量中的 id 获取 unicode 字符?

How do I get a unicode character from an id in a variable?

我正在尝试生成包含每个 Unicode 变量的文件。我已经能够将 unicode 提高到 U+FFFF,但是我需要将它提高到 U+231F4。我试过搜索答案,但当符号 id 位于变量中而不是仅键入时,其中 none 有效。

现在,我有这个:

for (int i = 0; i < 143860; i++) {
            System.out.println((char)i);
        }

它不是上升到 U+231F4,而是上升到 U+FFFF,并在它要打印到的文档中循环。我如何让它转到更高的 Unicode ID?

[ OP 说“我需要把它提高到 U+231F4”,我回答了这个问题。但他们的意思是他们想要打印 Unicode 定义的 143,859 个代码点。见另一个答案。我现在不能删除这个 它已被接受。 ]

Java 字符串不是由 Unicode 代码点组成,而是由 UTF-16 代码单元组成。您需要为 U+FFFF 以上的 Unicode 代码点使用代理对。例如,

     U+0   ⇒   0x0000            ⎫
     U+1   ⇒   0x0001            ⎪
           ⋮                      ⎬ Character in the BMP result
  U+D7FE   ⇒   0xD7FE            ⎪   in a single UTF-16 code unit.
  U+D7FF   ⇒   0xD7FF            ⎭

  U+D800   ⇒   ------            ⎫
  U+D801   ⇒   ------            ⎪
           ⋮                      ⎬ Can't be encoded using UTF-16.
  U+DFFE   ⇒   ------            ⎪   Illegal for interchange for this reason.
  U+DFFF   ⇒   ------            ⎭

  U+E000   ⇒   0xE000            ⎫
  U+E001   ⇒   0xE001            ⎪
           ⋮                      ⎬ Character in the BMP result
  U+FFFE   ⇒   0xFFFE            ⎪   in a single UTF-16 code unit.
  U+FFFF   ⇒   0xFFFF            ⎭

 U+10000   ⇒   0xD800, 0xDC00    ⎫
 U+10001   ⇒   0xD800, 0xDC01    ⎪
           ⋮                      ⎬ Those outside result in two.
 U+231F2   ⇒   0xD84C, 0xDDF2    ⎪
 U+231F3   ⇒   0xD84C, 0xDDF3    ⎭

 U+231F4   ⇒   0xD84C, 0xDDF4    ⎫
 U+231F5   ⇒   0xD84C, 0xDDF5    ⎪
           ⋮                      ⎬ We don't care about these.
U+10FFFE   ⇒   0xDBFF, 0xDFFE    ⎪
U+10FFFF   ⇒   0xDBFF, 0xDFFF    ⎭

有关代理对的详细信息,您可以参考 UTF-16 的 Wikipedia 页面。

解决方案 1:printf %c

这些细节并不重要,因为我们可以使用 printf %c 将 Unicode 代码点编码为 UTF-16 代码单元。 (感谢@VGR。)

for (int cp=0; cp<0x231F4; ++cp) {
   if (cp < 0xD800 || cp >= 0xE000) {
      System.out.printf("%c%n", cp);
   }
}

优化:

for (int cp=0; cp<0xD800; ++cp) {
   System.out.println((char)cp);
}

for (int cp=0xE000; cp<0x10000; ++cp) {
   System.out.println((char)cp);
}

for (int cp=0x10000; cp<0x231F4; ++cp) {
   System.out.printf("%c%n", cp);
}

解决方案 2:Character.toChars

或者,我们可以使用 Character.toChars(codePoint) 生成包含 Unicode 代码点的 UTF-16 代码单元的 char[]

for (int cp=0; cp<0x231F4; ++cp) {
   if (cp < 0xD800 || cp >= 0xE000) {
      System.out.println(Character.toChars(cp));
   }
}

优化:

for (int cp=0; cp<0xD800; ++cp) {
   System.out.println((char)cp);
}

for (int cp=0xE000; cp<0x10000; ++cp) {
   System.out.println((char)cp);
}

for (int cp=0x10000; cp<0x231F4; ++cp) {
   System.out.println(Character.toChars(cp));
}

我相信上面仍然创建了很多数组。自己实施转换可以避免这种情况,因此应该更快。

// Up to but excluding U+231F4 ⇒ 0xD84C, 0xDDF4

for (int cp=0; cp<0xD800; ++cp) {
   System.out.println((char)cp);
}

for (int cp=0xE000; cp<0x10000; ++cp) {
   System.out.println((char)cp);
}

char pair[2];
for (int hisurro=0xD800; hisurro<0xD84C; ++hisurro) 
   pair[0] = (char)hisurro;
   for (int losurro=0xDC00; losurro<0xE000; ++losurro) 
      pair[1] = (char)losurro;
      System.out.println(pair);
   }
}

pair[0] = 0xD84C;
for (int losurro=0xDC00; losurro<0xDDF4; ++losurro) 
   pair[1] = (char)losurro;
   System.out.println(pair);
}

请注意,您的终端无法完全读取结果。输出包括不可打印的字符(例如控制字符)、标记(与其他字符组合)、未分配的代码点、私人使用的代码点等

tl;博士

这是单行本。 (为了好玩,我不推荐。)

IntStream
        .rangeClosed( 0 , Character.MAX_CODE_POINT )
        .filter(
                codePoint ->
                        !
                                List
                                        .of( Character.CONTROL , Character.FORMAT , Character.SURROGATE , Character.PRIVATE_USE , Character.UNASSIGNED )
                                        .contains( ( byte ) Character.getType( codePoint ) )
        )
        .forEach(
           codePoint -> System.out.println( codePoint + " code point is named: " + Character.getName( codePoint ) + " = " + Character.toString( codePoint ) )
        )
;

当运行.

32 code point is named: SPACE =  
33 code point is named: EXCLAMATION MARK = !
34 code point is named: QUOTATION MARK = "
…
917997 code point is named: VARIATION SELECTOR-254 = 
917998 code point is named: VARIATION SELECTOR-255 = 
917999 code point is named: VARIATION SELECTOR-256 = 

避免char

Java 中的 char 类型已过时。该数据类型甚至无法表示 Unicode 中定义并受 Java.

支持的字符的一半

Java 现在完全支持代码点。不幸的是,这种支持并不明显,多年后在 CharacterStringStringBuilder classes 中被固定在老化的 API 上。您必须回顾涉及 char.

的过时方法

使用代码点

养成使用 Unicode code point integers, without any char. A code point is a number assigned to each and every one of the 143,859 characters defined by Unicode 的习惯。

这些代码点编号的分配范围为 0 到 10FFFF 十六进制,0 到 1,114,111 十进制。显然,这百万范围内的大部分是空的,目前未分配或留作私人使用的储备。

你说:

however I need to get it up to U+231F4.

不,您需要转到 U+10FFFF(十进制为 1,114,111)。

顺便说一下,Unicode 一直在增长。所以,不要执着于字符数,例如 143,860。我们永远不会有太多的表情符号!还有一些严肃的角色也在添加中。

所以你的循环:

for (int i = 0; i < 143_860; i++) {  // NO! Wrong limit. 

…需要将其限制从 143_860 更改为 1_114_111 十进制(10FFFF 十六进制)。

for (int i = 0; i < 1_114_111; i++) { // YES! Correct limit.

或者,对于此限制,使用常量 Character.MAX_CODE_POINT

for (int i = 0; i < Character.MAX_CODE_POINT; i++) {  // Use named constant rather than "magic" mystery number.

还有一点……MAX_CODE_POINT 包含 ,所以我们应该测试“小于或等于”而不是“小于”。将 < 更改为 <=

for (int i = 0; i <= Character.MAX_CODE_POINT; i++) {  // Use named constant rather than "magic" mystery number.

Character class can tell us if a code point is valid 还是不行。从 0 到最大值 1,114,111 的所有代码点都是有效数字。负数和超过最大值的数字无效。

同样的 class 可以告诉我们代码点代表什么样的字符。 Unicode 标准定义了 30 种,正式名称为“通用类别”。这些类别在 Character class 上定义为命名常量,不幸的是与其他不同的常量混合在一起。

我们想跳过其中的某些类别,特别是五个:

  • Character.CONTROL
  • Character.FORMAT
  • Character.SURROGATE
  • Character.PRIVATE_USE
  • Character.UNASSIGNED

要确定代码点的类别,请调用 Character.getType。不幸的是,该方法 returns 只是 int 而不是专用的枚举对象。

如上所列,Characterclass定义了各种byte常量,用于一般类别,但没有办法方便地将getType返回的数字转换成类别名称。请参阅书籍上的相关问题 How to get the category name of the character type in Java?. There is a feature-request,但尚未实施。所以我们必须自己动手。

这里我们使用名为 unicodeGeneralCategoryCodesToAvoidByte 个对象的列表,一个元素对应五个感兴趣的常量。

要从代码点 int 数字移动到实际字符,请调用 Character.toString( codePoint ) 以生成包含单个字符的 String

要获取 Unicode 标准定义的字符的正式名称,请调用 Character.getName( codePoint )

List < Byte > unicodeGeneralCategoryCodesToAvoid = List.of( Character.CONTROL , Character.FORMAT , Character.SURROGATE , Character.PRIVATE_USE , Character.UNASSIGNED );
for ( int codePoint = 0 ; codePoint <= Character.MAX_CODE_POINT ; codePoint++ ) {
    if ( Character.isValidCodePoint( codePoint ) )    // If code point is valid.
    {
        if ( unicodeGeneralCategoryCodesToAvoid.contains( ( byte ) Character.getType( codePoint ) ) ) // If control character or if unassigned code point, skip it.
        {
            // No code needed. Skip over this code point as it does not represent a printable character.
        } else {
            System.out.println( codePoint + " code point is named: " + Character.getName( codePoint ) + " = " + Character.toString( codePoint ) );
        }
    } else {
        System.out.println( "ERROR - Invalid code point number: " + codePoint );
    }
}

当运行.

INFO - Demo starting. 
32 code point is named: SPACE =  
33 code point is named: EXCLAMATION MARK = !
34 code point is named: QUOTATION MARK = "
35 code point is named: NUMBER SIGN = #
36 code point is named: DOLLAR SIGN = $
37 code point is named: PERCENT SIGN = %
…
123 code point is named: LEFT CURLY BRACKET = {
124 code point is named: VERTICAL LINE = |
125 code point is named: RIGHT CURLY BRACKET = }
126 code point is named: TILDE = ~
160 code point is named: NO-BREAK SPACE =  
161 code point is named: INVERTED EXCLAMATION MARK = ¡
162 code point is named: CENT SIGN = ¢
…
917997 code point is named: VARIATION SELECTOR-254 = 
917998 code point is named: VARIATION SELECTOR-255 = 
917999 code point is named: VARIATION SELECTOR-256 = 
INFO - Demo ending. 

有关我处理 Unicode 通用类别的更多代码,请参阅 my Answer to the Question, How to get the category name of the character type in Java?