如何从变量中的 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 现在完全支持代码点。不幸的是,这种支持并不明显,多年后在 Character
、String
和 StringBuilder
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
而不是专用的枚举对象。
如上所列,Character
class定义了各种byte
常量,用于一般类别,但没有办法方便地将getType
返回的数字转换成类别名称。请参阅书籍上的相关问题 How to get the category name of the character type in Java?. There is a feature-request,但尚未实施。所以我们必须自己动手。
这里我们使用名为 unicodeGeneralCategoryCodesToAvoid
的 Byte
个对象的列表,一个元素对应五个感兴趣的常量。
要从代码点 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?。
我正在尝试生成包含每个 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 现在完全支持代码点。不幸的是,这种支持并不明显,多年后在 Character
、String
和 StringBuilder
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
而不是专用的枚举对象。
如上所列,Character
class定义了各种byte
常量,用于一般类别,但没有办法方便地将getType
返回的数字转换成类别名称。请参阅书籍上的相关问题 How to get the category name of the character type in Java?. There is a feature-request,但尚未实施。所以我们必须自己动手。
这里我们使用名为 unicodeGeneralCategoryCodesToAvoid
的 Byte
个对象的列表,一个元素对应五个感兴趣的常量。
要从代码点 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?。