无法识别Java字符串

时间:2017-07-25 09:32:08

标签: java string character-encoding

我无法识别devā́n等字符串中的代理字符。我在这里阅读了有关这个主题的相关问题,但是这个问题仍有问题...... 如你所见,这个字符串的“自然”长度(我只是构成了这个表达式)是5,但"devā́n".length()给了我6. 这很好,因为ā́内部包含两个字符(它不包含UTF-16代码范围)。但是我希望得到字符串的长度,就像你读它或打印一样,所以在这种情况下5

我尝试使用以下技巧herehere识别怪人字符,但它不起作用,我总是得到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

1 个答案:

答案 0 :(得分:2)

首先,769(U + 0301)未作为代理字符进行测试的原因是它不是代理字符。当Unicode代码点在平面0之外时,使用代理字符以UTF-16表示。 (代理是代码单位,范围为U + D800到U + DFFF。)

所以你真正要做的就是弄清楚UTF-16字符串中有多少“普通”字符。这分两步完成:

  • 首先,使用Normalizer API将字符串规范化为NFC格式(请参阅Normalizing Text)。
  • 然后使用String API查找代码点的数量 字符串;例如使用String.codePointCountjavadoc)。

在这种情况下,这仍然失败。原因是代码点序列

ā: 257
́: 769

实际上代表一个带有两个变音符号的“a”字符。这不能表示为单个Unicode代码点,因此它的NFC是两个代码点。

更令人困惑的是,典型的渲染器会在后面的字符上显示“锐”重音。所以它看起来就好像你的例子中有一个“非常严重”。

处理像这样的病态例子是非常困难的,其中基本字符具有多个可能变得奇怪的变音符号。也许您需要转换为NFD,然后计算不是变音符号的代码点。