【问题标题】:How to get a reversed String (unicode safe)如何获得一个反转的字符串(unicode 安全)
【发布时间】:2011-11-20 13:12:16
【问题描述】:

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

它的 unicode 是“\u0061\u0301\u0065”。

还原它的幼稚方法是逐个字符

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);
}

我正在检查每个字符是字母、数字还是 ISO 控制。如果不是,我假设它应该与前一个字符粘在一起。

问题是,我还应该检查或担心其他事情吗?我的做法还幼稚吗?

【问题讨论】:

  • 为什么有人想这样做? :-)
  • 是否可以容忍规范化形式的变化?
  • 您绝不能使用charAt。它坏了。 你只是非法颠倒了你所有的代理人!!!
  • @pablosaraiva 您必须通过字形边界,而不是代码点边界,当然也不是代码单元边界来遍历它。看起来icu.text.BreakIterator.getCharacterInstance 可以用来构建一些东西,但我没有尝试过。在 Perl 中,它支持扩展的字素集群要容易得多:只需 reverse /(\X)/g。如果你不这样做,你还可以反转 CRLF 组合,足够的错误。

标签: java string unicode


【解决方案1】:

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

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

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

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

感谢 tchrist 指出 ICU 对文本分割的支持,包括扩展的字素簇,例如在下面的 cmets 中确定的那个(参见 virama)。 This resource 似乎是这类东西的权威信息来源。

【讨论】:

  • 这确实是一个很好的方法,适用于我给你的输入,但它不能满足以下字符串:सरस्वती。
  • Mike,我原以为 NFC 的规范化是可行的方法,因此每个代理对最有可能代表预期的抽象字符...
  • @Mike:你必须使用 ICU。 OraSun 做不到。此外,您不能使用String.reverse()StringBuffer.reverse(),因为您破坏了所有代理项。尝试翻转MATHEMATICAL FRAKTUR CAPITAL ACOMBINING DOT ABOVEMATHEMATICAL FRAKTUR CAPITAL B:U+1D504 U+0307 U+1D505。正确答案是MATHEMATICAL FRAKTUR CAPITAL BMATHEMATICAL FRAKTUR CAPITAL ACOMBINING DOT ABOVE:U+1D505 U+1D504 U+0307。天真的方式翻转代理并创建非法的非字符序列。
  • 规范化听起来不对:将碱基+修饰符对转换为单个字符显然是一项遗留操作,并且通常没有给定序列的任何复合字符修饰符——这就是将基本字符与修饰符分开的重点。所以我会完全放弃这种方法,而是寻找一个能够理解组合字符的合适的 Unicode 库。
  • @Mike:看来您需要使用来自icu.text.BreakInteratorgetCharacterInstance 方法。天哪,多么可怕的痛苦。我习惯于在 Perl 中只做reverse /(\X)/g
猜你喜欢
  • 2010-09-30
  • 1970-01-01
  • 2017-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-06
  • 2011-01-01
相关资源
最近更新 更多