How to replace case-insensitive literal substrings in Java
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));
J. Lin
Updated on December 02, 2020Comments
-
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 about 12 yearsWhat if target contains $ or diacritical characters like á?
-
lukastymo about 12 yearswhat do you mean? target=FOOBará, after replacing in above way output is: Bará
-
stracktracer about 12 yearsI 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 about 11 yearsYou can also pass the Locale into toUpperCase(locale) if your dealing with characters like á.
-
Danubian Sailor over 10 yearsYou can't turn string into regex matching it so simple. It's not correct generally, it will work only for specific cases.
-
gstackoverflow about 10 years@smas Is it regex java syntax?
-
Jeff Adamson almost 9 yearsUse 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 over 8 yearsAwesome, This is working with only only
replaceAll
andreplaceFirst
-
msteiger over 8 yearsWorks great! Note that "target" must not be null. Clearing sbSourceLower should not be necessary (any more).
-
ed22 over 6 yearsJust making sure. Pattern.quote("foo") is not necessary if the string is "foo" right? Only if it is something more fancy, right?
-
yetanothercoder over 6 yearsThanks 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 about 4 yearsthis method is utterly slow as its complexity is O(size_str * size_findtext)
-
Ajeetkumar almost 3 yearsis this better (in performance) than regex based solutions?
-
TungHarry over 2 yearsThis function is good and easy to understand