2017-07-25 85 views
1

我無法識別字符串中的替代字符,如devā́n。正如你所看到的,這個字符串的「自然」長度(我剛剛構成表達式)是5,但是"devā́n".length()給出了這個字符串的「自然」長度我6.
這很好,因爲ā́內部包含兩個字符(它不是UTF-16代碼範圍)。但是我想要讀取字符串的長度或者打印出來,所以在這種情況下請使用5無法識別Java字符串中的替代字符

我試圖識別具有以下技巧的怪人字符發現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)); 
} 

是否可能可能ā́不是一對有效的代理字符?我如何識別這樣的複合字符並將其計爲只有一個?

順便說一句的上面的代碼輸出是

String: devā́n 
Length: 6 
Codepoints: 6 
false 
false 


d: 100 
e: 101 
v: 118 
ā: 257 
́: 769 
n: 110 

回答

2

首先,即769(U + 0301)不測試作爲替代字符的原因是,它不是一個替代字符。當Unicode碼位於平面之外時,使用代理字符0以UTF-16表示。 (代理單元的範圍是U + D800到U + DFFF範圍內的代碼單元。)

因此,您真正想要做的是在UTF-16字符串中計算出有多少「普通」字符。這兩個步驟完成:

  • 首先,規範化字符串形式的NFC使用Normalizer API(見Normalizing Text)。
  • 然後使用String API查找中的代碼點數中的 的字符串;例如使用String.codePointCountjavadoc)。

在這種情況下,這仍然失敗。原因在於代碼點序列

ā: 257 
́: 769 

實際上表示一個帶有兩個變音標記的「a」字符。這不能表示爲一個Unicode碼點,所以NFC就是兩個碼點。

更令人困惑的是,一個典型的渲染器將在下面的字符上顯示「尖銳」的口音。所以它看起來就像你在你的例子中有一個「n急性」。

要處理像這樣的病態示例是非常困難的,其中基本字符具有可能呈現奇怪的多個變音符。也許你需要翻譯成NFD,然後計算不是變音符的代碼點。

+0

謝謝,這聽起來合乎邏輯。但's = Normalizer.normalize(s,Form.NFC);'和'System.out.println(s.codePointCount(0,s.length()));'仍然給我'6'。我究竟做錯了什麼? – mumpitz

+0

謝謝!我用's = Normalizer.normalize(s,Form.NFD);'和's = s.replaceAll(「\\ W」,「」);'測試了它,現在它的長度確實是5。 – mumpitz