从字符串中删除所有出现的char


311

我可以用这个:

String str = "TextX Xto modifyX";
str = str.replace('X','');//that does not work because there is no such character ''

有没有办法X从Java中的字符串中删除所有出现的字符?

我试过了,这不是我想要的: str.replace('X',' '); //replace with space


3
您是否尝试过替换单个字符的字符串?
peter.murray.rust 2011年

Answers:


523

尝试使用需要CharSequence参数(例如String)而不是的重载char

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

2
第一个参数是正则表达式,有时不能按预期运行,特别是如果此字符串来自用户输入。
vbezhenar

9
@vsb:不正确。该特定超载的两个参数均为CharSequencedocs.oracle.com/javase/7/docs/api/java/lang/...
LukeH

如果X是char类型,该怎么办?
KNU

7
@Kunal:我想您toString首先需要这样做。因此您的代码应类似于str = str.replace(yourChar.toString(), "");
LukeH 2014年

请注意,您可以使用Unicode转义str = str.replace("\uffff", "");

42

使用

public String replaceAll(String regex, String replacement)

将工作。

用法是str.replace("X", "");

执行中

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

返回:

lakjsdf xx

6
除非您只限于支持Java 1.4,否则Regex可能会为此而矫kill过正-因为版本1.5包含replace一个简单的重载CharSequence
2011年

3
@LukeH,这是String.replace的反编译源。它使用正则表达式。我同意正则表达式感觉很沉重,但是即使上面接受的答案也是如此。公共字符串替换(CharSequence var1,CharSequence var2)}
Perry Tew


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

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

这是我从字符串中删除字符的示例。


4
这是非常低效的,特别是与公认的答案相比。
埃里克·罗伯逊

3
我认为此答案有效,但正确答案更短且更快
evilReiko 2012年

2

我喜欢在这种情况下使用RegEx:

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

g表示全局,因此它将遍历您的整个字符串并将所有X替换为''; 如果要同时替换X和x,则只需说:

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

(在这里查看我的小提琴:小提琴


我想这可能会奏效,但正确答案的执行速度更快和更短的,它总是最好避免正则表达式尽可能因为它的已知比其他方法更慢
evilReiko

2

您好,请在下面尝试此代码

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怎么办?不错的解决方案!
蒙娜·贾拉勒

2

使用replaceAll代替replace

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

这应该给您想要的答案。


replace使用replaceAll结束。查看实现。这是实现String#replace的方式:return Pattern.compile(target.toString(), Pattern.LITERAL).matcher( this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
Sal_Vader_808

0
package com.acn.demo.action;

public class RemoveCharFromString {

    static String input = "";
    public static void main(String[] args) {
        input = "abadbbeb34erterb";
        char token = 'b';
        removeChar(token);
    }

    private static void removeChar(char token) {
        // TODO Auto-generated method stub
        System.out.println(input);
        for (int i=0;i<input.length();i++) {
            if (input.charAt(i) == token) {
            input = input.replace(input.charAt(i), ' ');
                System.out.println("MATCH FOUND");
            }
            input = input.replaceAll(" ", "");
            System.out.println(input);
        }
    }
}

input = "deletes all blanks too";给“ deletesalllankstoo”
卡普兰

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(),删除字符串时所得的String 永远不会变得大于起始String。因此,它避免了重复分配和追加的同时字符的明智复制StringBuilderreplace()一样。
更不用说不需要删除的PatternMatcher实例的无意义生成了replace()
replace()此解决方案不同的是,可以一次删除几个字符。


Lambdas / Functional Programming目前非常时髦,但是恕不接受恕我直言,用它来创建比所选答案长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注释。总而言之,比我的髋部 lambda解决方案长3倍。这里很好地解释了为什么我的臀部 lambda解决方案也更快:为什么Java的String :: replace()这么慢?
卡普兰

就其本身而言:如果确实是解决方案的大小,那么其他两倍的解决方案或需要外部库的解决方案将更适合批评。自Java 8以来,多年来一直是该语言一部分的语言扩展并不是很时髦。评分系统的一个普遍问题是,时间因素比解决方案的质量更重。结果,在后三分之二中越来越多地发现了更新的,有时甚至更好的解决方案。
卡普兰

就代码而不是执行速度而言,我指的是10倍以上。每次调用regex模式都会编译得慢得多。如果要频繁使用此类正则表达式,您确实需要缓存已编译的匹配器并重用(OP并未说明使用了哪种方案-可能是罕见的从表单提交中清除数据的方案,也可能用于紧凑的情况)每秒被调用1000次)。
Volksman

关于性能问题,我添加了一个新的答案,该答案针对提供的各种答案进行了快速基准测试。如果OP经常执行此操作,则应避免使用String.replace()选项,因为在引擎盖下重复重新编译正则表达式模式非常昂贵。
Volksman

0

通过性能基准评估主要答案,这证实了人们对当前选择的答案会使引擎盖下正则表达式操作昂贵的担忧

到目前为止,提供的答案有3种主要样式(忽略JavaScript答案;)):

  • 使用String.replace(charsToDelete,“”); 在引擎盖下使用正则表达式
  • 使用Lambda
  • 使用简单的Java实现

就代码大小而言,显然String.replace是最简洁的。简单的Java实现比Lambda稍微更小,更干净(IMHO)(请不要误解-我经常在合适的地方使用Lambda)

执行速度从快到慢依次为:简单的Java实现,Lambda,然后是String.replace()(调用regex)。

到目前为止,最快的实现是对简单的Java实现进行了调整,以便它可以将StringBuilder缓冲区预分配为最大可能的结果长度,然后仅将不在“要删除的字符”字符串中的字符追加到缓冲区中。这样可以避免在长度大于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函数包装为成员函数”-出于在基准场景中进行调用的目的,以使其与其他实现的调用方式一致。
大众

我只是意识到OP要求删除所有出现的单个字符,但是您的答案更改了处理一组字符的范围。我使用的“已接受”答案实现没有,也从未打算满足多个字符。因此,我更新了上述基准以反映此基准时间。顺便说一句,如果您想扩大范围以支持多个字符,则多次调用替换非常昂贵。最好切换到单个调用replaceAll(“ [XYZ]”,“”)
Volksman

解决方案中所示的函数仅在被调用时被初始化一次。除了将函数定义包装到函数调用之外,将函数定义包装到成员函数中的唯一作用是扭曲基准。
卡普兰

由于每次调用的方差都很大,因此通过一次调用正确地对快速持续时间方法进行基准测试几乎是不可能的。因此,基准测试通常涉及多次重复调用同一方法,然后对总时间进行评估,以与替代方法的总时间进行比较(或根据需要计算平均值)
。– Volksman,


-3

您可以使用str = str.replace("X", "");前面提到的方法,这样会很好的。供您参考的''字符不是空(或有效)字符,而是'\0'

因此,您可以str = str.replace('X', '\0');改用。


9
这是不正确的。'\ 0'将产生一个实际的空字符。str.replace('X','\ 0')等同于str.replace(“ X”,“ \ u0000”)完全不是OP想要的
-Andrey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.