从字符串中删除所有指定字符

350

我可以使用这个:

String str = "TextX Xto modifyX";
str = str.replace('X','');//that does not work because there is no such character ''
有没有一种方法可以从Java中删除所有出现的字符X? 我尝试了这个,但这不是我想要的:str.replace('X',' '); //用空格替换

3
你尝试过替换单个字符的字符串吗? - peter.murray.rust
13个回答

571

2
第一个参数是正则表达式,有时可能会出现意外情况,特别是如果该字符串来自用户输入。 - vbezhenar
9
不是这样的。该重载的两个参数都是“CharSequence”。http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace%28java.lang.CharSequence,%20java.lang.CharSequence%29 - LukeH
如果 X 是 char 类型,该怎么办? - KNU
7
@Kunal:我想你需要先使用toString方法。所以你的代码应该像这样:str = str.replace(yourChar.toString(), ""); - LukeH
注意,您可以使用Unicode转义,例如不删除非字符str = str.replace("\uffff", ""); - Jaime Hablutzel
显示剩余5条评论

47

使用

public String replaceAll(String regex, String replacement)

这将有效。

使用方法如下:str.replace("X", "");

执行:

"Xlakjsdf Xxx".replaceAll("X", "");

返回:

lakjsdf xx

6
除非你必须支持Java 1.4版本,否则使用正则表达式可能有点过度 - 因为从1.5版本开始,有一个接受简单CharSequencereplace重载方法。 - LukeH
3
这是String.replace的反编译源码,它使用了正则表达式。我同意正则表达式会让代码感觉较重,但即使对于上面被接受的答案,在幕后也是使用了正则表达式。下面是翻译的结果: 这段代码是String.replace方法的反编译源码,其中使用了正则表达式。尽管正则表达式可能会使程序变得复杂,但即使对于之前被接受的答案,背后也使用了正则表达式。 - Perry Tew
1
谢天谢地,这已经不再是真的了。它不再使用正则表达式。 - Nand

29
如果您想使用Java字符串做一些操作,可以参考Commons Lang StringUtils
StringUtils.remove("TextX Xto modifyX", 'X');

1
可能是因为它看起来更清晰,所以正好符合我要找的东西,与“replace”相比。 - Line

6
String test = "09-09-2012";
String arr [] = test.split("-");
String ans = "";

for(String t : arr)
    ans+=t;

这是一个示例,我已将字符串中的“-”字符删除。


4
这非常低效,特别是与被接受的答案相比较。 - Erick Robertson
3
我认为这个答案是可行的,但正确的答案更短更快。 - evilReiko
只需使用replace来替换分割。此外,您不应在循环中使用+=运算符。 - Oskar

3

你好,请尝试下面的代码:

public class RemoveCharacter {

    public static void main(String[] args){
        String str = "MXy nameX iXs farXazX";
        char x = 'X';
        System.out.println(removeChr(str,x));
    }

    public static String removeChr(String str, char x){
        StringBuilder strBuilder = new StringBuilder();
        char[] rmString = str.toCharArray();
        for(int i=0; i<rmString.length; i++){
            if(rmString[i] == x){

            } else {
                strBuilder.append(rmString[i]);
            }
        }
        return strBuilder.toString();
    }
}

如果我们不是用x而是另一个字符串,你会怎么做呢?好的解决方案! - Mona Jalal

3
评估主要答案的性能基准,确认当前选择的答案在内部执行昂贵的正则表达式操作 迄今为止,提供的答案有三种主要风格(忽略JavaScript答案 ;)):
  • 使用String.replace(charsToDelete,"");这会在内部使用正则表达式
  • 使用Lambda
  • 使用简单的Java实现
从代码大小来看,String.replace明显是最简洁的。简单的Java实现比Lambda稍微小一些,并且更加干净(依我之见)(不要误解 - 我经常在适当的情况下使用Lambda)
按照速度的顺序,最快的是简单的Java实现,其次是Lambda,然后是String.replace()(调用正则表达式)。
迄今为止最快的实现是简单的Java实现,它被调整为将StringBuilder缓冲区预分配到最大可能的结果长度,然后只需将未包含在“chars to delete”字符串中的字符附加到缓冲区即可。这避免了任何对于长度> 16个字符的字符串(StringBuilder的默认分配)会发生重新分配的情况,并且避免了在Lambda实现中删除字符串的副本时出现的“向左滑动”性能损失。
以下代码运行了一个简单的基准测试,将每个实现运行1,000,000次并记录经过的时间。
确切的结果因每次运行而异,但性能顺序永远不会改变。
Start simple Java implementation
Time: 157 ms
Start Lambda implementation
Time: 253 ms
Start String.replace implementation
Time: 634 ms

Lambda实现(从Kaplan的答案中复制)可能会更慢,因为它对右侧所有字符进行“左移一位”。对于需要删除大量字符的较长字符串,这显然会变得更糟。此外,Lambda实现本身可能存在一些开销。
String.replace实现使用正则表达式,并在每次调用时执行正则表达式“编译”。优化方法是直接使用正则表达式并缓存编译模式,以避免每次编译的成本。
package com.sample;

import java.util.function.BiFunction;
import java.util.stream.IntStream;

public class Main {

    static public String deleteCharsSimple(String fromString, String charsToDelete)
    {
        StringBuilder buf = new StringBuilder(fromString.length()); // Preallocate to max possible result length
        for(int i = 0; i < fromString.length(); i++)
            if (charsToDelete.indexOf(fromString.charAt(i)) < 0)
                buf.append(fromString.charAt(i));   // char not in chars to delete so add it
        return buf.toString();
    }

    static public String deleteCharsLambda(String fromString1, String charsToDelete)
    {
        BiFunction<String, String, String> deleteChars = (fromString, chars) -> {
            StringBuilder buf = new StringBuilder(fromString);
            IntStream.range(0, buf.length()).forEach(i -> {
                while (i < buf.length() && chars.indexOf(buf.charAt(i)) >= 0)
                    buf.deleteCharAt(i);
            });
            return (buf.toString());
        };

        return deleteChars.apply(fromString1, charsToDelete);
    }

    static public String deleteCharsReplace(String fromString, String charsToDelete)
    {
        return fromString.replace(charsToDelete, "");
    }


    public static void main(String[] args)
    {
        String str = "XXXTextX XXto modifyX";
        String charsToDelete = "X";  // Should only be one char as per OP's requirement

        long start, end;

        System.out.println("Start simple");
        start = System.currentTimeMillis();

        for (int i = 0; i < 1000000; i++)
            deleteCharsSimple(str, charsToDelete);

        end = System.currentTimeMillis();
        System.out.println("Time: " + (end - start));

        System.out.println("Start lambda");
        start = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++)
            deleteCharsLambda(str, charsToDelete);

        end = System.currentTimeMillis();
        System.out.println("Time: " + (end - start));

        System.out.println("Start replace");
        start = System.currentTimeMillis();

        for (int i = 0; i < 1000000; i++)
            deleteCharsReplace(str, charsToDelete);

        end = System.currentTimeMillis();
        System.out.println("Time: " + (end - start));
    }
}

如果lambda函数被按照预期调用,时间如下所示(没有人将Lambda函数包装成成员函数)。 另外,您的deleteCharsReplace()实现是错误的:它替换了一个字符串“XYZ”,而不是需要的'X'、'Y'和'Z', 需要这样:fromString.replace("X", "").replace("Y", "").replace("Z", "");。 现在我们得到了正确的时间: 开始简单 时间:759 | 开始lambda 时间:1092 | 开始deleteCharsLambda() 时间:1420 | 开始修正替换 时间:4636 - Kaplan
除了在基准测试场景中调用以与其他实现方式相一致,否则没有人会将lambda函数包装成成员函数。 - Volksman
我刚意识到OP询问如何删除所有出现的单个字符,但是你的答案改变了范围以处理一组字符。我使用的“已接受”答案实现不会也从未旨在为多个字符提供服务。因此,我已更新上面的基准测试以反映这一点和基准测试时间。 顺便说一下,如果您想扩大范围以支持多个字符,则调用多次replace成本高昂。最好切换到单个调用replaceAll(“[XYZ]”,“”) - Volksman
当调用函数时,如 解决方案 中所示的函数仅被初始化一次。另外将函数定义和函数调用包装到成员函数中的唯一效果是扭曲基准。 - Kaplan
通过单次调用很难对快速持续时间方法进行正确的基准测试,因为每次调用的差异非常大。因此,基准测试通常涉及对同一方法进行多次重复调用,然后评估总时间以与替代方案的总时间进行比较(或者如果需要计算平均时间)。 - Volksman
在正常使用中,这样的方法通常会出现在一个长方法调用链的末尾,例如,从文件中处理数百万记录,并且执行此步骤仅是每个记录处理过程中的一个阶段,因此肯定会从另一个方法调用,而不是直接内联在某个单一的“巨型循环超级方法”(反模式)中,在开始时初始化一次。所有实现都通过外部方法调用进行了测试以模拟这种情况。如果预编译并缓存所选答案的模式并重用其匹配器,则速度也会更快。 - Volksman

2

使用replaceAll替代replace

str = str.replaceAll("X,"");

这应该可以给您所需要的答案。

1
使用replaceAll替换ends up。查看实现方式。这是String#replace的实现方式:return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( this).replaceAll(Matcher.quoteReplacement(replacement.toString())); - Molasses

2

在这种情况下,我喜欢使用正则表达式:

str = str.replace(/X/g, '');

这里的g代表全局,所以它将遍历整个字符串并将所有的X替换为''; 如果你想同时替换X和x,只需输入:

str = str.replace(/X|x/g, '');

(这里查看我的示例:fiddle

我猜这可能会起作用,但正确的答案执行速度更快、更短,尽可能避免使用正则表达式,因为它比其他方法慢。 - evilReiko

1

在替换时,您需要将需要删除的字符放在方括号内。示例代码如下:

String s = "$116.42".replaceAll("[$]", "");

0

这是一个 Lambda 函数,它可以删除传递的所有字符。

BiFunction<String,String,String> deleteChars = (fromString, chars) -> {
  StringBuilder buf = new StringBuilder( fromString );
  IntStream.range( 0, buf.length() ).forEach( i -> {
    while( i < buf.length() && chars.indexOf( buf.charAt( i ) ) >= 0 )
      buf.deleteCharAt( i );
  } );
  return( buf.toString() );
};

String str = "TextX XYto modifyZ";
deleteChars.apply( str, "XYZ" ); // –> "Text to modify"

这个解决方案考虑到了在删除字符时,结果字符串永远不会比起始字符串更大,因此避免了像replace()一样重复分配和复制,而是逐个字符地附加到StringBuilder中。
更不用说在replace()中无意义地生成PatternMatcher实例,这些实例在删除时从未被使用。
replace()不同,这个解决方案可以一次性删除多个字符。


1
使用Lambda/函数式编程现在非常流行,但是使用它创建一个比选定答案长10倍的解决方案在我看来是无法被证明的,因此我会下投反对票。 - Volksman
str.replace("…", "") 实例化了 private Pattern(…),然后在生成的模式上调用 public String replaceAll(String repl)。因此,以下函数调用发生了:return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( this).replaceAll(Matcher.quoteReplacement(replacement.toString())); - 请参见 Sal_Vader_808 的评论。总体而言,这比我 hip lambda 解决方案多大约三倍。这里很好地解释了为什么我的 hip lambda 解决方案也更快:为什么Java的String::replace()如此缓慢? - Kaplan
如果真的关注解决方案的规模,那么一些两倍于当前解决方案或需要外部库的解决方案更适合被批评。自Java 8以来已经成为语言一部分的语言扩展并不是最时髦的。评分系统的一个普遍问题是时间因素比解决方案的质量更加重要。结果是,更新的甚至更好的解决方案越来越多地出现在后三分之一。 - Kaplan
我所指的是代码长度,而不是执行速度增加了10倍。每次调用时编译正则表达式模式的任何内容都可能会慢得多。如果频繁使用此类正则表达式,则确实需要缓存已编译的匹配器并重复使用(OP没有说明它用于什么场景-可能是清理表单提交数据的罕见场景,也可能在被调用1000次/秒的紧密循环中使用)。 - Volksman
你上面的第一条评论将这个问题从“方便的解决方案”讨论转变为了运行时性能问题。一旦你把注意力转向性能问题,你必须意识到这标志着SO世界中的“拳脚相加”的情况 :) 无论如何,如果你想讨论相对性能,原生Java实现的157毫秒比你的Lambda实现的253毫秒快37%。 - Volksman
显示剩余2条评论

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