如何“正确”地将一个码点与Java字符进行比较?例如:
int codepoint = String.codePointAt(0);
char token = '\n';
我知道我可能可以这样做:if (codepoint==(int) token)
{ ... }
但是这段代码看起来比较脆弱。是否有正式的API方法可以用于比较 codepoints
和 chars
,或将 char
转换为 codepoint
以进行比较?
char
类型基于原始的 "Unicode 88" 规范,该规范仅限于16位。一年后,当实现Unicode 2.0时,引入了代理字符的概念,以超越16位的限制。String
。对于超过U+FFFF的码点,使用代理对来表示该码点,即第一个为高代理项代码单元(在范围 \uD800-\uDBFF 内),第二个为低代理项代码单元(在范围 \uDC00-\uDFFF 内)的两个char
。Character
方法都是基于这样一个假设:一个码点可以用一个char
表示,因此方法签名看起来是这样的。我想为了保持向后兼容性,在Unicode 2.0出现时没有改变,处理它们时需要谨慎。引用自Java文档:
像您在示例中所做的那样,将 char
强制转换为 int
是可行的。
Character类包含许多有用的方法来处理Unicode码点。需要注意的是,像Character.toChars(int)这样返回字符数组的方法。如果您的代码点位于补充范围内,则该数组的长度将为两个字符。
如何比较这些值取决于您是否想支持完整的Unicode值范围。以下示例代码可用于遍历字符串的代码点,测试是否存在补充字符 MATHEMATICAL_FRAKTUR_CAPITAL_G(𝔊 - U+1D50A)的匹配项:
public final class CodePointIterator {
private final String sequence;
private int index = 0;
public CodePointIterator(String sequence) {
this.sequence = sequence;
}
public boolean hasNext() {
return index < sequence.length();
}
public int next() {
int codePoint = sequence.codePointAt(index);
index += Character.charCount(codePoint);
return codePoint;
}
public static void main(String[] args) {
String sample = "A" + "\uD835\uDD0A" + "B" + "C";
int match = 0x1D50A;
CodePointIterator pointIterator = new CodePointIterator(sample);
while (pointIterator.hasNext()) {
System.out.println(match == pointIterator.next());
}
}
}
从Java 8开始,可以使用CharSequence.codePoints()方法:
public static void main(String[] args) {
String sample = "A" + "\uD835\uDD0A" + "B" + "C";
int match = 0x1D50A;
sample.codePoints()
.forEach(cp -> System.out.println(cp == match));
}
我创建了一个表格,以便更好地处理需要处理的Unicode字符串长度和比较情况。
next()
的主体可以编写为 int codePoint = sequence.codePointAt(index); index += Character.charCount(codePoint); return codePoint;
,这样可能会更易读且稍微更有效率一点。 - Steve Waring如果一个字符可以用单个字符表示(16位,基本多语言平面),你只需要将字符转换为整数来获取码点(正如问题所建议的那样),因此无需使用特殊方法进行转换。
如果您要比较字符和码点,则不需要任何特殊情况。只需将字符直接与整数进行比较即可(正如问题所建议的那样)。如果整数代表基本多语言平面之外的码点,则结果将始终为false。
对于基本多语言平面中的字符,将char强制转换为int将获得代码点。这对应于可以编码为单个16位char值的所有Unicode值。超出此平面的值(其代码点超过0xffff)无法表示为单个字符。这可能是为什么没有Character.toCodePoint(char value)的原因。
Java使用16位(UTF-16)模型处理字符,因此任何代码点> 0xFFFF的字符都将使用两个代理字符作为一对16位字符存储在字符串中,以表示平面和平面内的字符。
如果您想根据完整的Unicode标准正确处理字符和字符串,则需要考虑这一点来处理字符串。
XML非常关注此问题;访问Xerces中的XMLChar类(随Java版本5.0及更高版本一起提供)可用于与字符相关的代码。
看看Saxon XSLT/XQuery处理器也是有益的,因为作为一个行为良好的XML应用程序,它必须考虑Java如何在字符串中存储代码点。XQuery 1.0和XPath 2.0具有codepoints-to-string和string-to-codepoints函数;获取Saxon的副本并尝试使用它们来了解它们的工作方式可能是有益的。
char
转换为int
完全是不必要的。 - David Conrad