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

pab*_*iva 20 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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mik*_*lov 4

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

\n\n

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

\n\n

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

\n\n

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

\n\n

感谢 tchrist 指出 ICU 对文本分割的支持,包括扩展的字素簇,例如下面评论中指出的(请参阅 virama)。 该资源似乎是此类内容的权威信息来源。

\n

  • 规范化听起来不对:将基本+修饰符对转换为单个字符显然是一个遗留操作,并且通常对于给定的修饰符序列“没有”任何复合字符——这就是分离基本字符的全部意义来自修饰符。所以我会完全放弃这种方法,而是寻找一个能够理解字符组合的合适的 Unicode 库。 (3认同)
  • @Mike:看来您需要使用“icu.text.BreakInterator”中的“getCharacterInstance”方法。天哪,多么痛苦啊。我习惯于在 Perl 中执行“reverse /(\X)/g”。 (3认同)