如何在Java中去除代理字符?

14

我遇到了一个情况,保存到MySQL 5.1的文本中出现了代理字符。由于UTF-16在该版本中不受支持,因此我想在将其保存到数据库之前通过Java方法手动删除这些代理对。

目前我编写了以下方法,但我想知道是否有直接且最佳的处理方式。

非常感谢您的帮助。

public static String removeSurrogates(String query) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < query.length() - 1; i++) {
        char firstChar = query.charAt(i);
        char nextChar = query.charAt(i+1);
        if (Character.isSurrogatePair(firstChar, nextChar) == false) {
            sb.append(firstChar);
        } else {
            i++;
        }
    }
    if (Character.isHighSurrogate(query.charAt(query.length() - 1)) == false
            && Character.isLowSurrogate(query.charAt(query.length() - 1)) == false) {
        sb.append(query.charAt(query.length() - 1));
    }

    return sb.toString();
}
5个回答

10

以下是几个建议:

  • Character.isSurrogate(char c)

    当且仅当一个 char 值是低代理项或高代理项时,它就是代理项代码单元。

  • 检查代理项对似乎是毫无意义的,为什么不直接删除所有代理项呢?

  • x == false 相当于 !x

  • StringBuilder 在你不需要同步的情况下更好(比如变量永远不会离开局部范围)。

我建议使用这些方式:

public static String removeSurrogates(String query) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < query.length(); i++) {
        char c = query.charAt(i);
        // !isSurrogate(c) in Java 7
        if (!(Character.isHighSurrogate(c) || Character.isLowSurrogate(c))) {
            sb.append(firstChar);
        }
    }
    return sb.toString();
}

分解 if语句

您询问了以下语句:

if (!(Character.isHighSurrogate(c) || Character.isLowSurrogate(c))) {
    sb.append(firstChar);
}

一种理解方法是将每个操作拆分为其自身的函数,这样您就可以看到组合实现了您所期望的效果:

static boolean isSurrogate(char c) {
    return Character.isHighSurrogate(c) || Character.isLowSurrogate(c);
}

static boolean isNotSurrogate(char c) {
    return !isSurrogate(c);
}

...

if (isNotSurrogate(c)) {
    sb.append(firstChar);
}

我正在使用Jdk 1.6.0,并且无法找到内置的Character.isSurrogate(c)方法。它是否存在还是仅作为示例给出? - Slowcoder
@Slowcoder 显然这是在Java 7中添加的。我切换到适用于Java 6的版本。您可以将该语句解读为“不是高代理也不是低代理”,而不是(在我看来更复杂的)“不是高代理且不是低代理”。 - Brendan Long
如果"c"是一个低代理字符,这段代码将因为OR条件而将该字符添加到"sb"中。我的理解对吗? - Slowcoder
@Slowcoder 不是的,请检查括号。如果 isLowSurrogate(c) 为真,则 isHighSurrogate(c) || isLowSurrogate(c) 也为真(因为 x || true 为真),因此 !(isHighSurrogate(c) || isLowSurrogate(c)) 为假,因此不会被附加。如果这太复杂了,可以使用另一个版本,但我建议学习如何处理复杂的逻辑语句,因为它们有时会出现(我在我的哲学学分中上了一门逻辑课程,它非常有用)。 - Brendan Long
我将其分解为函数,每个步骤都很简单。当逻辑语句变得过于复杂以至于难以理解时,这是我建议采取的做法。 - Brendan Long
非常抱歉,我没有注意到低代理缺少否定。现在它完全有意义了。谢谢。 - Slowcoder

7

Java字符串以16位字符序列的形式存储,但它们所代表的是Unicode字符序列。在Unicode术语中,它们被存储为代码单元,但模拟代码点。因此,在讨论删除不存在于字符/代码点表示中的代理项时,这有点毫无意义(除非您有流氓单个代理项,那么您有其他问题)。

相反,您想要做的是删除将需要编码代理项的任何字符。这意味着任何位于基本多文种平面之外的字符。您可以使用简单的正则表达式来实现:

return query.replaceAll("[^\u0000-\uffff]", "");

2
为什么不简单地这样做?
for (int i = 0; i < query.length(); i++) 
    char c = query.charAt(i);
    if(!isHighSurrogate(c) && !isLowSurrogate(c))
        sb.append(c);

你可能应该用“?”替换它们,而不是直接删除它们。


非常有帮助,谢谢。所以我认为逐个字符迭代是唯一的删除方法,没有直接的方法可以获取一个字符串作为参数并返回已删除代理项的字符串。我是对的吗? - Slowcoder
这样的方法在JDK中不存在。 - irreputable

1

只是好奇。如果 char 是高代理项,是否需要检查下一个字符?它应该是低代理项。修改后的版本如下:

public static String removeSurrogates(String query) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < query.length(); i++) {
        char ch = query.charAt(i);
        if (Character.isHighSurrogate(ch))
            i++;//skip the next char is it's supposed to be low surrogate
        else
            sb.append(ch);
    }    
    return sb.toString();
}

0

如果是删除,所有这些解决方案都很有用,但如果是替换,下面的方法更好。

StringBuffer sb = new StringBuffer();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if(Character.isHighSurrogate(c)){
            sb.append('*');
        }else if(!Character.isLowSurrogate(c)){
            sb.append(c);
        }
    }
    return sb.toString();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接