如何在Java中没有正则表达式的情况下只替换一次字符串?

时间:2009-10-24 04:57:28

标签: java string

我需要用一个更大的字符串替换一个动态子字符串,但只需要一次(即第一次匹配)。 String类仅提供replace(),它替换子字符串的所有实例;有一个replaceFirst()方法,但它只需要regexp而不是常规字符串。使用正则表达式时我有两个问题:

1)我的子串是动态的,因此可能包含奇怪的字符,这些字符在正则表达式中表示其他东西,我不想处理字符转义。

2)这种替换经常发生,我不确定使用正则表达式是否会影响性能。我无法预先编译正则表达式,因为正则表达式本身是动态的!

我必须在这里遗漏一些东西,因为在我看来这是一个非常基本的东西......是否有一个replaceFirst方法在java franework中的其他地方使用常规字符串?

7 个答案:

答案 0 :(得分:26)

您应该使用已经过测试且记录良好的库来支持编写自己的代码!

StringUtils.replaceOnce("aba", "a", "")    = "ba"

StringUtils类来自Apache Commons Lang3包,可以像这样在Maven中导入:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.8.1</version>
</dependency>

答案 1 :(得分:15)

正如劳伦斯建议的那样,你可以像这样使用Pattern.quote

newString = string.replaceFirst(Pattern.quote(substring), replacement);

这会创建一个与子字符串字面匹配的新正则表达式。另一种方法是简单地将子字符串编译为文字正则表达式,如下所示:

newString = Pattern.compile(substring, Pattern.LITERAL).
        matcher(string).replaceFirst(replacement);

这可能稍微有点效率,但也不太清楚。

你也可以手动完成,因为你只想替换第一次出现。但是正则表达式非常有效,所以不要过早优化!

答案 2 :(得分:13)

使用bigString.indexof(smallString)获取大字符串中第一次出现的小字符串的索引(如果没有,则返回-1,在这种情况下,您已完成)。然后,使用bigString.substring来获取匹配前后的大字符串,最后concat将这些字符串放回到一起,然后将其替换为中间。

答案 3 :(得分:5)

请务必同时替换替换中的\s$s。这可能是您想要的,因为您不能将任何子组()移除。)

newStr = str.replaceFirst(Pattern.quote(target), Matcher.quoteReplacement(replacement));

答案 4 :(得分:2)

java中的StringBuilder类可以非常容易地用于基于非正则表达式替换一个字符串与另一个字符串。

private static String replace(String in, String ths, String that) {

    StringBuilder sb = new StringBuilder(in);

    int idx = sb.indexOf(ths); 

    while (idx > -1)
        sb.replace(idx, idx + ths.length(), that);
        idx = sb.indexOf(ths);
    }

    return sb.toString(); 

}

答案 5 :(得分:2)

Pattern

的解决方案

您还可以更改使用文字字符解释的String.replace方法,以仅替换第一次出现的target字符序列:

/**
 * Replaces the first subsequence of the <tt>source</tt> character sequence
 * that matches the literal target sequence with the specified literal
 * replacement sequence.
 * 
 * @param source source sequence on which the replacement is made
 * @param target the sequence of char values to be replaced
 * @param replacement the replacement sequence of char values
 * @return the resulting string
 */
private static String replaceFirst1(CharSequence source, CharSequence target, CharSequence replacement) {
    return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
        source).replaceFirst(Matcher.quoteReplacement(replacement.toString()));
}


来自Pattern.LITERAL的文档:

  

指定此标志后,指定模式的输入字符串将被视为文字字符序列。 输入序列中的元字符或转义序列没有特殊含义。


没有Pattern

的解决方案

另一种更有效的方法当然是使用Alex Martelli的提示来产生以下功能:

/**
 * Replaces the first subsequence of the <tt>source</tt> string that matches
 * the literal target string with the specified literal replacement string.
 * 
 * @param source source string on which the replacement is made
 * @param target the string to be replaced
 * @param replacement the replacement string
 * @return the resulting string
 */
private static String replaceFirst2(String source, String target, String replacement) {
    int index = source.indexOf(target);
    if (index == -1) {
        return source;
    }

    return source.substring(0, index)
        .concat(replacement)
        .concat(source.substring(index+target.length()));
}


时间测量

基于10次运行,replaceFirst2方法比replaceFirst1方法执行快5倍。我已将这两种方法放在一个循环中,重复次数为100.000,并在几毫秒内得到以下结果:

    Method                 Results (in ms)             Average
replaceFirst1: 220 187 249 186 199 211 172 199 281 199 | 210
replaceFirst2:  40  39  58  45  48  40  42  42  43  59 |  45

答案 6 :(得分:1)

Pattern.quote似乎并不适用于所有情况。试试`Pattern.quote(“:-)”);