前几天遇到了生僻字的问题。很有意思。在不同的平台上显示的字符不一样,有些是方块有些是?有些干脆就是一个表情图标。
检查了编码没有问题,是UTF-8。
研究了一下最终发现了原因。
出错的字符,落到了UNICODE的私人造字区里面,参考如下。
http://jicheng.tw/hanzi/unicode.html
不同的系统/字体,不能保证私人造字区的一致性。
自己写了段测试代码,发现目前Unicode Base区,扩展ABCD均没有问题,唯独私人造字区不行。
解决方法也比较简单,显示字符之前进行检测就行了,如果发现客户姓名里面有私人造字区的字,那么不显示“姓名,您好”而是显示“亲爱的客户,您好”。
代码如下。注意,在Unicode扩展区,一个字符是由两个\u 表示的。也就是说String类的标准方法charAt等等不适用了,要采用codePointAt, codePointCount来进行字符的相关操作。
public class RareCharacterUtility {
public static boolean containsUserDefinedUnicode(String string) {
if (string == null) {
throw new NullPointerException("Stirng must be non-null");
}
int[] code = toCodePointArray(string);
// U+E000..U+F8FF
for (int c : code) {
if (c >= '\ue000' && c <= '\uf8ff') {
return true;
}
}
return false;
}
static int[] toCodePointArray(String str) {
int len = str.length();
int[] acp = new int[str.codePointCount(0, len)];
for (int i = 0, j = 0; i < len; i = str.offsetByCodePoints(i, 1)) {
acp[j++] = str.codePointAt(i);
}
return acp;
}
static String toHex(int[] chars) {
String r = "[";
for (int i=0; i<chars.length; i++) {
if (r.length() > 1) {
r += ",";
}
r += Integer.toHexString(chars[i]);
}
r += "]";
return r;
}
public static void main(String[] argu) {
String rr = ("\u5f20\ue0bf\uD86C\uDE70\uD840\uDC10\uD86D\uDF44\uD87E\uDCAC\u9fc6");
System.out.println("Unicode = " + toHex(toCodePointArray(rr)));
boolean r = (containsUserDefinedUnicode(rr));
System.out.println("Test result = " + r + " should be true");
}
}