如何获得反向字符串(unicode安全)

时间:2011-09-19 19:54:52

标签: java string unicode

让我们假设我们想要恢复以下字符串“áe”。

unicode就是“\ u0061 \ u0301 \ u0065”。

恢复它的天真的方法将是char的char

private static String reverseStringNaive(String s) {
    char[] characters = new char[s.length()];
    for (int i = s.length() - 1; i >= 0; i--) {
        int j = s.length() - i - 1;
        characters[j] = s.charAt(i); 
    }
    return new String(characters);
}

当我们希望获得“eá”(\ u0065 \ u0061 \ u0301)时,它会给我们“éa”(\ u0065 \ u0301 \ u0061)。精确的“'”应该与“a”结合在一起,而不是改为“e”。

以下代码为我提供了该字符串的预期结果:

private static String reverseString(String s) {
    char[] characters = new char[s.length()];
    for (int i = s.length() - 1; i >= 0; i--) {
        int j = s.length() - i - 1;
        if (Character.isLetterOrDigit(s.charAt(i)) || Character.isISOControl(s.charAt(i))) {
            characters[j] = s.charAt(i); 
        } else {
            characters[j] = s.charAt(i-1);
            characters[j+1] = s.charAt(i);
            i--;
        }
    }
    return new String(characters);
}

我正在检查每个角色是否为Letter,Digit或ISO Control。如果没有,我认为它应该与前一个角色保持一致。

问题是,还有其他我应该检查或担心的事情吗?我的aproach仍然天真吗?

1 个答案:

答案 0 :(得分:4)

您的问题也可以通过将字符串转换为规范分解形式NFC来解决。基本上,java.text.Normalizer类可用于将重音和其他组合字符与其基本字符组合在一起,以便您能够正确反转。

所有这些其他想法(String.reverse(),StringBuffer.reverse())将正确地反转缓冲区中的字符,但如果你从分解的字符开始,你可能得不到你期望的:)。

在某些“分解形式”中,重音符号与其基本形式(作为单独的字符)分开存储,但在“组合”形式中则不是。因此,在一种形式中,“áe”存储为三个字符,而在另一种形式中,组合形式存储为两个。

但是,这样的规范化不足以处理其他类型的字符组合,也不能解释Unicode星体平面中的字符,这些字符在Java中存储为两个字符(或更多?)。

感谢tchrist指出ICU对文本分段的支持,包括扩展的字形集群,例如下面评论中指出的那些(见virama)。 This resource似乎是关于此类内容的权威信息来源。