How to replace case-insensitive literal substrings in Java

127,908

Solution 1

String target = "FOOBar";
target = target.replaceAll("(?i)foo", "");
System.out.println(target);

Output:

Bar

It's worth mentioning that replaceAll treats the first argument as a regex pattern, which can cause unexpected results. To solve this, also use Pattern.quote as suggested in the comments.

Solution 2

If you don't care about case, then you perhaps it doesn't matter if it returns all upcase:

target.toUpperCase().replace("FOO", "");

Solution 3

Regular expressions are quite complex to manage due to the fact that some characters are reserved: for example, "foo.bar".replaceAll(".") produces an empty string, because the dot means "anything" If you want to replace only the point should be indicated as a parameter "\\.".

A simpler solution is to use StringBuilder objects to search and replace text. It takes two: one that contains the text in lowercase version while the second contains the original version. The search is performed on the lowercase contents and the index detected will also replace the original text.

public class LowerCaseReplace 
{
    public static String replace(String source, String target, String replacement)
    {
        StringBuilder sbSource = new StringBuilder(source);
        StringBuilder sbSourceLower = new StringBuilder(source.toLowerCase());
        String searchString = target.toLowerCase();

        int idx = 0;
        while((idx = sbSourceLower.indexOf(searchString, idx)) != -1) {
            sbSource.replace(idx, idx + searchString.length(), replacement);
            sbSourceLower.replace(idx, idx + searchString.length(), replacement);
            idx+= replacement.length();
        }
        sbSourceLower.setLength(0);
        sbSourceLower.trimToSize();
        sbSourceLower = null;

        return sbSource.toString();
    }


    public static void main(String[] args)
    {
        System.out.println(replace("xXXxyyyXxxuuuuoooo", "xx", "**"));
        System.out.println(replace("FOoBaR", "bar", "*"));
    }
}

Solution 4

Not as elegant perhaps as other approaches but it's pretty solid and easy to follow, esp. for people newer to Java. One thing that gets me about the String class is this: It's been around for a very long time and while it supports a global replace with regexp and a global replace with Strings (via CharSequences), that last doesn't have a simple boolean parameter: 'isCaseInsensitive'. Really, you'd've thought that just by adding that one little switch, all the trouble its absence causes for beginners especially could have been avoided. Now on JDK 7, String still doesn't support this one little addition!

Well anyway, I'll stop griping. For everyone in particular newer to Java, here's your cut-and-paste deus ex machina. As I said, not as elegant and won't win you any slick coding prizes, but it works and is reliable. Any comments, feel free to contribute. (Yes, I know, StringBuffer is probably a better choice of managing the two character string mutation lines, but it's easy enough to swap the techniques.)

public String replaceAll(String findtxt, String replacetxt, String str, 
        boolean isCaseInsensitive) {
    if (str == null) {
        return null;
    }
    if (findtxt == null || findtxt.length() == 0) {
        return str;
    }
    if (findtxt.length() > str.length()) {
        return str;
    }
    int counter = 0;
    String thesubstr = "";
    while ((counter < str.length()) 
            && (str.substring(counter).length() >= findtxt.length())) {
        thesubstr = str.substring(counter, counter + findtxt.length());
        if (isCaseInsensitive) {
            if (thesubstr.equalsIgnoreCase(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                // Failing to increment counter by replacetxt.length() leaves you open
                // to an infinite-replacement loop scenario: Go to replace "a" with "aa" but
                // increment counter by only 1 and you'll be replacing 'a's forever.
                counter += replacetxt.length();
            } else {
                counter++; // No match so move on to the next character from
                           // which to check for a findtxt string match.
            }
        } else {
            if (thesubstr.equals(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                counter += replacetxt.length();
            } else {
                counter++;
            }
        }
    }
    return str;
}

Solution 5

Just make it simple without third party libraries:

    final String source = "FooBar";
    final String target = "Foo";
    final String replacement = "";
    final String result = Pattern.compile(target, Pattern.LITERAL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(source)
.replaceAll(Matcher.quoteReplacement(replacement));
Share:
127,908
J. Lin
Author by

J. Lin

Updated on December 02, 2020

Comments

  • J. Lin
    J. Lin over 3 years

    Using the method replace(CharSequence target, CharSequence replacement) in String, how can I make the target case-insensitive?

    For example, the way it works right now:

    String target = "FooBar";
    target.replace("Foo", "") // would return "Bar"
    
    String target = "fooBar";
    target.replace("Foo", "") // would return "fooBar"
    

    How can I make it so replace (or if there is a more suitable method) is case-insensitive so that both examples return "Bar"?

  • stracktracer
    stracktracer about 12 years
    What if target contains $ or diacritical characters like á?
  • lukastymo
    lukastymo about 12 years
    what do you mean? target=FOOBará, after replacing in above way output is: Bará
  • stracktracer
    stracktracer about 12 years
    I mean two things: 1. "blÁÜ123".replaceAll("(?i)bláü") does not replace anything. 2. "Sentence!End".replaceAll("(?i)Sentence.") does maybe replace more than anticipated.
  • rob
    rob about 11 years
    You can also pass the Locale into toUpperCase(locale) if your dealing with characters like á.
  • Danubian Sailor
    Danubian Sailor over 10 years
    You can't turn string into regex matching it so simple. It's not correct generally, it will work only for specific cases.
  • gstackoverflow
    gstackoverflow about 10 years
    @smas Is it regex java syntax?
  • Jeff Adamson
    Jeff Adamson almost 9 years
    Use Pattern.quote() to protect the search string from being interpreted as a regex. This doe snot address the unicode quirks listed above, but should be fine for basic character sets. e.g. target.replaceAll("(?i)"+Pattern.quote("foo"), "");
  • Pratik Butani
    Pratik Butani over 8 years
    Awesome, This is working with only only replaceAll and replaceFirst
  • msteiger
    msteiger over 8 years
    Works great! Note that "target" must not be null. Clearing sbSourceLower should not be necessary (any more).
  • ed22
    ed22 over 6 years
    Just making sure. Pattern.quote("foo") is not necessary if the string is "foo" right? Only if it is something more fancy, right?
  • yetanothercoder
    yetanothercoder over 6 years
    Thanks for concise solution and thanks to @msteiger for correction. I wonder why nobody added similar solution to any famous lib like Guava, Apache Commons etc.?
  • Mladen Adamovic
    Mladen Adamovic about 4 years
    this method is utterly slow as its complexity is O(size_str * size_findtext)
  • Ajeetkumar
    Ajeetkumar almost 3 years
    is this better (in performance) than regex based solutions?
  • TungHarry
    TungHarry over 2 years
    This function is good and easy to understand