Looping through RegEx matches and replace the current match

Kawamoto Takeshi :

Consider the following string:

He ordered a pizza with anchovies. Unfortunately, it wasn't the thing he wanted. Besides, pizza with mushroom, pepperoni and anchovies is much better than the normal pizza with anchovies.

Let's say that you need to change pizza with (ingredients) to pizza with (ingredients) on a thin crust.

To do that, I had set a Regex:

(?i:pizza with [a-zA-Z,\s]*?anchovies)

This captures three matches. Then, I proceed to add the on a thin crust to each match using the following code:

Pattern p = Pattern.compile("(?i:pizza with [a-zA-Z,\s]*?anchovies)");
Matcher m = p.matcher(string);
while(m.find())
{
    string = string.replace(m.group(), m.group() + "on a thin crust.");
}

The output of this will then be:

He ordered a pizza with anchovies on a thin crust on a thin crust. Unfortunately, it wasn't the thing he wanted. Besides, pizza with mushroom, pepperoni and anchovies is much better than the normal pizza with anchovies on a thin crust on a thin crust.

What happened:

The first match pizza with anchovies is the same as the last match. Hence, using String.replace caused the first and last match to change to pizza with anchovies on a thin crust. Since, we are looping through all matches, the last match will still occur and the replace function will also replace the first match (since first match and last match are the same). Hence, we get double on a thin crust.

Q:

Is there a way to replace a Regex match at the current match only?

Wiktor Stribiżew :

Do it with a single replaceAll using $0 backreference to the whole match:

String s = "He ordered a pizza with anchovies. Unfortunately, it wasn't the thing he wanted. Besides, pizza with mushroom, pepperoni and anchovies is much better than the normal pizza with anchovies.";
s = s.replaceAll("(?i)pizza with [a-zA-Z,\\s]*?anchovies", "$0 on a thin crust");
System.out.println(s);
// => He ordered a pizza with anchovies on a thin crust. Unfortunately, it wasn't the thing 
//    he wanted. Besides, pizza with mushroom, pepperoni and anchovies on a thin crust is 
//    much better than the normal pizza with anchovies on a thin crust.

See the Java demo

This way, you will avoid replacing already replaced texts since replaceAll() process all, multiple, non-overlapping occurrences of the substrings meeting your regex pattern.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=469962&siteId=1