6

在 Java 中,从字符串中删除给定字符的最有效方法是什么?目前,我有这个代码:

private static String processWord(String x) {
    String tmp;

    tmp = x.toLowerCase();
    tmp = tmp.replace(",", "");
    tmp = tmp.replace(".", "");
    tmp = tmp.replace(";", "");
    tmp = tmp.replace("!", "");
    tmp = tmp.replace("?", "");
    tmp = tmp.replace("(", "");
    tmp = tmp.replace(")", "");
    tmp = tmp.replace("{", "");
    tmp = tmp.replace("}", "");
    tmp = tmp.replace("[", "");
    tmp = tmp.replace("]", "");
    tmp = tmp.replace("<", "");
    tmp = tmp.replace(">", "");
    tmp = tmp.replace("%", "");

    return tmp;
}

如果我使用某种 StringBuilder、正则表达式或其他东西会更快吗?是的,我知道:分析它并查看,但我希望有人可以提供他们头顶的答案,因为这是一项常见任务。

4

7 回答 7

18

尽管\\p{Punct}将指定比问题中更广泛的字符范围,但它确实允许更短的替换表达式:

tmp = tmp.replaceAll("\\p{Punct}+", "");
于 2013-07-08T16:22:54.777 回答
12

这是一个迟到的答案,只是为了好玩。

在这种情况下,我建议以可读性而非速度为目标。当然你可以超级可读但太慢,就像这个超级简洁的版本一样:

private static String processWord(String x) {
    return x.replaceAll("[][(){},.;!?<>%]", "");
}

这很慢,因为每次调用此方法时,都会编译正则表达式。所以你可以预编译正则表达式。

private static final Pattern UNDESIRABLES = Pattern.compile("[][(){},.;!?<>%]");

private static String processWord(String x) {
    return UNDESIRABLES.matcher(x).replaceAll("");
}

假设 JVM 的正则表达式引擎优化了字符类查找,这对于大多数用途来说应该足够快。这是我个人会使用的解决方案。

现在没有分析,我不知道您是否可以通过制作自己的角色(实际上是代码点)查找表来做得更好:

private static final boolean[] CHARS_TO_KEEP = new boolean[];

填充一次,然后迭代,生成结果字符串。我会把代码留给你。:)

同样,我不会深入研究这种优化。代码变得太难阅读了。性能有那么重要吗?还要记住,现代语言是 JITted 的,在热身后它们会表现得更好,所以使用一个好的分析器。

应该提到的一件事是原始问题中的示例性能非常差,因为您正在创建一大堆临时字符串!除非编译器优化所有这些,否则该特定解决方案的性能将最差。

于 2013-07-09T03:57:33.793 回答
5

你可以这样做:

static String RemovePunct(String input) 
{
    char[] output = new char[input.length()];
    int i = 0;

    for (char ch : input.toCharArray())
    {
        if (Character.isLetterOrDigit(ch) || Character.isWhitespace(ch)) 
        {
            output[i++] = ch;
        }        
    }

    return new String(output, 0, i);
}

// ...

String s = RemovePunct("This is (a) test string.");

如果您发现它们会因您的需要而变慢,那么这可能会比使用正则表达式执行得更好。

但是,如果您想要删除一长串不同的特殊字符,它可能会很快变得混乱。在这种情况下,正则表达式更容易处理。

http://ideone.com/mS8Irl

于 2013-07-08T16:39:33.960 回答
1

字符串是不可变的,因此尝试非常动态地使用它们并不好尝试使用 StringBuilder 而不是 String 并使用其所有出色的方法!它会让你做任何你想做的事。另外,是的,如果您有想做的事情,请找出它的正则表达式,它会为您工作得更好。

于 2013-07-08T16:19:21.427 回答
0

String#replaceAll(String regex, String replacement)用作_

tmp = tmp.replaceAll("[,.;!?(){}\\[\\]<>%]", "");

System.out.println(
   "f,i.l;t!e?r(e)d {s}t[r]i<n>g%".replaceAll(
                   "[,.;!?(){}\\[\\]<>%]", "")); // prints "filtered string"
于 2013-07-08T16:21:46.960 回答
0

现在您的代码将遍历所有字符tmp并将它们与您要删除的所有可能字符进行比较,因此它将使用
number of tmp charactersxnumber or characters you want to remove比较。

要优化您的代码,您可以使用短路 OR||并执行类似的操作

StringBuilder sb = new StringBuilder();
for (char c : tmp.toCharArray()) {
    if (!(c == ',' || c == '.' || c == ';' || c == '!' || c == '?'
            || c == '(' || c == ')' || c == '{' || c == '}' || c == '['
            || c == ']' || c == '<' || c == '>' || c == '%'))
        sb.append(c);
}
tmp = sb.toString();

或者像这样

StringBuilder sb = new StringBuilder();
char[] badChars = ",.;!?(){}[]<>%".toCharArray();

outer: 
for (char strChar : tmp.toCharArray()) {
    for (char badChar : badChars) {
        if (badChar == strChar)
            continue outer;// we skip `strChar` since it is bad character
    }
    sb.append(strChar);
}
tmp = sb.toString();

这样,您将遍历每个tmp字符,但如果不是,该字符的比较次数可能会减少%(因为这将是最后一次比较,如果字符是.程序将在一次比较中得到他的结果)。


如果我没记错的话,这种方法与字符类( [...]) 一起使用,所以也许可以这样尝试

Pattern p = Pattern.compile("[,.;!?(){}\\[\\]<>%]"); //store it somewhere so 
                                         //you wont need to compile it again
tmp = p.matcher(tmp).replaceAll("");
于 2013-07-08T17:26:22.770 回答
-1

你可以这样做:

tmp.replaceAll("\\W", "");

删除标点符号

于 2013-07-08T16:20:15.930 回答