我无法识别devā́n
等字符串中的代理字符。我在这里阅读了有关这个主题的相关问题,但是这个问题仍有问题......
如你所见,这个字符串的“自然”长度(我只是构成了这个表达式)是5,但"devā́n".length()
给了我6.
这很好,因为ā́
内部包含两个字符(它不包含UTF-16代码范围)。但是我希望得到字符串的长度,就像你读它或打印一样,所以在这种情况下5
。
我尝试使用以下技巧here和here识别怪人字符,但它不起作用,我总是得到6.只是看看这个:
//string containing surrogate pair
String s = "devā́n";
//prints the string properly
System.out.println("String: " + s);
//prints "Length: 6"
System.out.println("Length: " + s.length());
//prints "Codepoints: 6"
System.out.println("Codepoints: " + s.codePointCount(0, s.length()));
//false
System.out.println(
Character.isSurrogate(s.charAt(3)));
//false
System.out.println(
Character.isSurrogate(s.charAt(4)));
//six code points
System.out.println("\n");
for (int i = 0; i < s.length(); i++) {
System.out.println(s.charAt(i) + ": " + s.codePointAt(i));
}
ā́
可能不是一对有效的代理字符吗?我如何识别这样的复合字符并将其统计为只有一个?
BTW上面代码的输出是
String: devā́n
Length: 6
Codepoints: 6
false
false
d: 100
e: 101
v: 118
ā: 257
́: 769
n: 110
答案 0 :(得分:2)
首先,769(U + 0301)未作为代理字符进行测试的原因是它不是代理字符。当Unicode代码点在平面0之外时,使用代理字符以UTF-16表示。 (代理是代码单位,范围为U + D800到U + DFFF。)
所以你真正要做的就是弄清楚UTF-16字符串中有多少“普通”字符。这分两步完成:
Normalizer
API将字符串规范化为NFC格式(请参阅Normalizing Text)。 String
API查找代码点的数量
字符串;例如使用String.codePointCount
(javadoc)。在这种情况下,这仍然失败。原因是代码点序列
ā: 257
́: 769
实际上代表一个带有两个变音符号的“a”字符。这不能表示为单个Unicode代码点,因此它的NFC是两个代码点。
更令人困惑的是,典型的渲染器会在后面的字符上显示“锐”重音。所以它看起来就好像你的例子中有一个“非常严重”。
处理像这样的病态例子是非常困难的,其中基本字符具有多个可能变得奇怪的变音符号。也许您需要转换为NFD,然后计算不是变音符号的代码点。