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字符进行判定,互换i和i+1位置
//String 对象以UTF-16保存 Unicode 字符,需要用2个字符表示一个超大字符集的汉字,这这种表示方式称之为 Surrogate,第一个字符叫 Surrogate High,第二个就是 Surrogate Low,因反转导致字单元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);
}
一个完整的 Unicode 字符叫代码点CodePoint,而一个 Java char 叫 代码单元 code unit。String 对象以UTF-16保存 Unicode 字符,需要用2个字符表示一个超大字符集的汉字,这这种表示方式称之为 Surrogate,第一个字符叫 Surrogate High,第二个就是 Surrogate Low。具体需要注意的事宜如下:
- 判断一个char是否是Surrogate区的字符,用Character的 isHighSurrogate()/isLowSurrogate()方法即可判断。从两个Surrogate High/Low 字符,返回一个完整的 Unicode CodePoint 用 Character.toCodePoint()/codePointAt()方法。
- 一个Code Point,可能需要一个也可能需要两个char表示,因此不能直接使用 CharSequence.length()方法直接返回一个字符串到底有多少个汉字,而需要用String.codePointCount()/Character.codePointCount()。
- 要定位字符串中的第N个字符,不能直接将N作为偏移量,而需要从字符串头部依次遍历得到,需要用String/Character.offsetByCodePoints() 方法。
- 从字符串的当前字符,找到上一个字符,也不能直接用offset-- 实现,而需要用 String.codePointBefore()/Character.codePointBefore(),或用 String/Character.offsetByCodePoints()
- 从当前字符,找下一个字符,不能直接用 offset++实现,需要判断当前 CodePoint的长度后,再计算得到,或用String/Character.offsetByCodePoints()。
参考: 文章