public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
//判斷字符是否為Unicode字符逗威,16位Unicode字符會占2位
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
//對Unicode字符進(jìn)行判定恒界,互換i和i+1位置
//String 對象以UTF-16保存 Unicode 字符鹉梨,需要用2個(gè)字符表示一個(gè)超大字符集的漢字鳖枕,這這種表示方式稱之為 Surrogate蝶怔,第一個(gè)字符叫 Surrogate High伟叛,第二個(gè)就是 Surrogate Low勃教,因反轉(zhuǎn)導(dǎo)致字單元char位置互換淤击,保持字符正確需全局遍歷將High和low位置修改正確
reverseAllValidSurrogatePairs();
}
return this;
}
private void reverseAllValidSurrogatePairs() {
for (int i = 0; i < count - 1; i++) {
char c2 = value[i];
if (Character.isLowSurrogate(c2)) {
char c1 = value[i + 1];
if (Character.isHighSurrogate(c1)) {
value[i++] = c1;
value[i] = c2;
}
}
}
}
public static boolean isSurrogate(char ch) {
return ch >= MIN_SURROGATE && ch < (MAX_SURROGATE + 1);
}
public static boolean isLowSurrogate(char ch) {
return ch >= MIN_LOW_SURROGATE && ch < (MAX_LOW_SURROGATE + 1);
}
public static boolean isHighSurrogate(char ch) {
// Help VM constant-fold; MAX_HIGH_SURROGATE + 1 == MIN_LOW_SURROGATE
return ch >= MIN_HIGH_SURROGATE && ch < (MAX_HIGH_SURROGATE + 1);
}
一個(gè)完整的 Unicode 字符叫代碼點(diǎn)CodePoint,而一個(gè) Java char 叫 代碼單元 code unit故源。String 對象以UTF-16保存 Unicode 字符污抬,需要用2個(gè)字符表示一個(gè)超大字符集的漢字,這這種表示方式稱之為 Surrogate心软,第一個(gè)字符叫 Surrogate High壕吹,第二個(gè)就是 Surrogate Low。具體需要注意的事宜如下:
- 判斷一個(gè)char是否是Surrogate區(qū)的字符删铃,用Character的 isHighSurrogate()/isLowSurrogate()方法即可判斷耳贬。從兩個(gè)Surrogate High/Low 字符,返回一個(gè)完整的 Unicode CodePoint 用 Character.toCodePoint()/codePointAt()方法猎唁。
- 一個(gè)Code Point咒劲,可能需要一個(gè)也可能需要兩個(gè)char表示,因此不能直接使用 CharSequence.length()方法直接返回一個(gè)字符串到底有多少個(gè)漢字诫隅,而需要用String.codePointCount()/Character.codePointCount()腐魂。
- 要定位字符串中的第N個(gè)字符,不能直接將N作為偏移量逐纬,而需要從字符串頭部依次遍歷得到蛔屹,需要用String/Character.offsetByCodePoints() 方法。
- 從字符串的當(dāng)前字符豁生,找到上一個(gè)字符兔毒,也不能直接用offset-- 實(shí)現(xiàn),而需要用 String.codePointBefore()/Character.codePointBefore()甸箱,或用 String/Character.offsetByCodePoints()
- 從當(dāng)前字符育叁,找下一個(gè)字符,不能直接用 offset++實(shí)現(xiàn)芍殖,需要判斷當(dāng)前 CodePoint的長度后豪嗽,再計(jì)算得到,或用String/Character.offsetByCodePoints()。
參考: 文章