如何将Unicode代码点转换为字符表示?

39

我该如何将代表码点的字符串转换为相应的字符?

例如,我想要一个函数,输入 U+00E4 并返回 ä

虽然在字符类中有一个接受整数的函数 toChars(int codePoint),但没有接受这种类型字符串的函数。

是否有内置函数可以使用,还是我需要对字符串进行某些转换以获取整数,然后再调用该函数?

9个回答

39

码点以十六进制数字为前缀的 U+ 写出。

因此,你可以这样做。

int codepoint=Integer.parseInt(yourString.substring(2),16);
char[] ch=Character.toChars(codepoint);

你能否获取单个Java字符而不是char数组? - k-den
4
是的,可以使用类似于 new StringBuilder().appendCodePoint(codepoint).toString().charAt(0) 的方法,但是请注意,超过 64k 的代码点会产生 两个 字符,高代理项和低代理项。您可能更喜欢去掉 .charAt(0) 直接将结果作为 String 获取。 - David Conrad

12

这个问题要求一个函数用于将代表Unicode码点的字符串值转换为相应的字符 (即"+Unnnn",而不是Java格式的"\unnnn""0xnnnn")。然而,Java的更新版本增加了一些功能,可以简化包含多个Unicode格式码点的字符串的处理:

这些增强功能允许使用不同的方法来解决OP提出的问题。这种方法可以在一个语句中将Unicode格式的一组码点转换为可读的String

void processUnicode() {

    // Create a test string containing "Hello World " with code points in Unicode format.
    // Include an invalid code point (+U0wxyz), and a code point outside the Unicode range (+U70FFFF).
    String data = "+U0048+U0065+U006c+U006c+U0wxyz+U006f+U0020+U0057+U70FFFF+U006f+U0072+U006c+U0000064+U20+U1f601";

    String text = Arrays.stream(data.split("\\+U"))
            .filter(s -> ! s.isEmpty()) // First element returned by split() is a zero length string.
            .map(s -> {
                try {
                    return Integer.parseInt(s, 16);
                } catch (NumberFormatException e) { 
                    System.out.println("Ignoring element [" + s + "]: NumberFormatException from parseInt(\"" + s + "\"}");
                }
                return null; // If the code point is not represented as a valid hex String.
            })
            .filter(v -> v != null) // Ignore syntactically invalid code points.
            .filter(i -> Character.isValidCodePoint(i)) // Ignore code points outside of Unicode range.
            .map(i -> Character.toString(i)) // Obtain the string value directly from the code point. (Requires JDK >= 11 )
            .collect(Collectors.joining());

    System.out.println(text); // Prints "Hello World "
}

这是输出结果:

run:
Ignoring element [0wxyz]: NumberFormatException from parseInt("0wxyz"}
Hello World 
BUILD SUCCESSFUL (total time: 0 seconds)

注:

  • 使用这种方法,不再需要特定的函数来转换Unicode格式的代码点。相反,通过多个中间操作在Stream处理中分散了这个需求。当然,同样的代码仍然可以用于处理Unicode格式中的单个代码点。
  • 可以轻松地添加中间操作以对Stream执行进一步的验证和处理,例如大小写转换、删除表情符号等。

在Java 8(或:Java 5及更高版本)中,您可以使用new String(Character.toChars(i))来代替Character.toString(i) - undefined
@MarkRotteveel 是的,那种方法更符合问题的精神,因为楼主特别提到了Character.toChars(int codePoint) - undefined

8

String 上调用 这个构造函数

"\u00E4"

new String(new int[] { 0x00E4 }, 0, 1);

你正在将它作为整型字面量处理。而操作数是以U+00E4字符串形式存在的。 - Anirudha
@Anirudh 没错,你已经适当地处理了它。但我想知道 "\u00e4" 是否被认为是等价的(也就是在 Java 源代码中)。我给你点赞。 - Joop Eggen
我猜它们是等价的..但是我不确定.. :) - Anirudha

7

从 Kotlin 转换:

    public String codepointToString(int cp) {
        StringBuilder sb = new StringBuilder();
        if (Character.isBmpCodePoint(cp)) {
            sb.append((char) cp);
        } else if (Character.isValidCodePoint(cp)) {
            sb.append(Character.highSurrogate(cp));
            sb.append(Character.lowSurrogate(cp));
        } else {
            sb.append('?');
        }
        return sb.toString();
    }

谢谢,但这实际上是@tateisu的代码通过转换器运行的。 - Qubei
好的,但是我需要知道如何获取/u字符串而不是实际值。 - nullsector76
请注意,StringBuilder具有.appendCodePoint()方法。 - esotericpig

2

这个例子没有使用char[]。

// this code is Kotlin, but you can write same thing in Java
val sb = StringBuilder()
val cp :Int // codepoint
when {
    Character.isBmpCodePoint(cp) -> sb.append(cp.toChar())
    Character.isValidCodePoint(cp) -> {
        sb.append(Character.highSurrogate(cp))
        sb.append(Character.lowSurrogate(cp))
    }
    else -> sb.append('?')
}

5
您可以直接使用stringBuilder.appendCodePoint(cp),而不需要这样做。 - Callum Rogers
我知道。我已经准备好这段代码来解释机制。 - tateisu

1
自Java 11以来,您可以执行以下操作:
jshell> Character.toString(Integer.parseInt("U+00E4".substring(2), 16))
$1 ==> "ä"

0
  • 您想显示一个代码点吗?
  • 或者
  • 您想将代码点转换为char数据类型吗?

嗯,第二部分是不可能的,因为代码点可能有4个字节,而char数据类型只能容纳2个字节。

因此,如果在Java中永远不使用char数据类型,使用int或String代替可能是更一般化的方法。

用于保存代码点的数据类型是什么? 单个代码点可以保存在int数据类型中。 Unicode字符串在技术上是int数组,而不是char数组。

String smiley = new String(new int[] { 0x1F600 }, 0, 1); //int[] array of int codepoints can be converted to string
System.out.println(" print smiley =  "+smiley );

输出 打印笑脸 =

如果您正在使用IntelliJ idea,您可以复制输出的笑脸并粘贴到双引号字符串中。 您将得到这个"\uD83D\uDE00"

如果您打印此字符串,您将再次获得一个笑脸

System.out.println("\uD83D\uDE00");

输出

为什么我们不能在字符串中使用单个"\u"来表示笑脸? 因为当设计\u转义时,所有Unicode字符都可以用2个字节或4个十六进制数字表示。因此,在Java字符串文字中,\u后始终有4个十六进制数字。要表示更大的Unicode值,您需要更大的十六进制数,但这将破坏现有的Java字符串。因此,Java使用与UTF-16相同的方法。

以下两者是等效的。

String smiley = new String(new int[] { 0x1F600 }, 0, 1); //using single code point number
String smiley = "\uD83D\uDE00";//split code point in 2 parts of 2 bytes each (utf-16)

请参考此处的Java文档。

https://docs.oracle.com/javase/7/docs/api/java/lang/Character.html#unicode

Unicode字符表示
char数据类型(因此Character对象封装的值)基于原始Unicode规范,该规范将字符定义为固定宽度的16位实体。 Unicode标准已经更改,以允许需要超过16位表示的字符。合法代码点的范围现在是U+0000到U+10FFFF,称为Unicode标量值(请参阅Unicode标准中U+n符号的定义)。
从U+0000到U+FFFF的字符集有时称为基本多语言平面(BMP)。代码点大于U+FFFF的字符称为补充字符。Java平台在char数组和String和StringBuffer类中使用UTF-16表示。在此表示中,补充字符表示为一对char值,第一个来自高代理范围(\uD800-\uDBFF),第二个来自低代理范围(\uDC00-\uDFFF)。
因此,char值表示基本多语言平面(BMP)代码点,包括代理代码点或UTF-16编码的代码单元。int值表示所有Unicode代码点,包括补充代码点。 int的较低(最低有效)21位用于表示Unicode代码点,而较高(最高有效)11位必须为零。除非另有说明,否则与补充字符和代理char值相关的行为如下:
仅接受char值的方法无法支持补充字符。它们将代理范围中的char值视为未定义字符。例如,Character.isLetter('\uD840')返回false,即使在字符串中跟随任何低代理值,此特定值也表示字母。接受int值的方法支持所有Unicode字符,包括补充字符。例如,Character.isLetter(0x2F81A)返回true,因为代码点值表示字母(CJK表意文字)。在Java SE API文档中,Unicode代码点用于U+0000和U+10FFFF之间的字符值,而Unicode代码单元用于UTF-16编码的16位char值作为代码单元。有关Unicode术语的更多信息,请参阅Unicode词汇表。

-6
你可以打印它们。
s='\u0645\u0635\u0631\u064a'
print(s)

-7
到目前为止,我发现最简单的方法就是将代码点强制转换;如果您只期望每个代码点有一个单一字符,那么这可能对您来说是可以接受的。
int codepoint = ...;
char c = (char)codepoint;

2
如果代码点高于char的最大值,则会溢出,从而得到错误的代码点。我不认为这是一个好主意。 - Douglas Held
在Java中,char类型已经过时了许多年(几十年?),无法表示Unicode定义的一半字符。 - Basil Bourque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接