4

I'm trying to replace first occurence of String matching my regex, while iterating those occurences like this:

(this code is very simplified, so don't try to find some bigger sense of it)

Matcher tagsMatcher = Pattern.compile("\\{[sdf]\\}").matcher(value);

int i = 0;
while (tagsMatcher.find()) {
    value = value.replaceFirst("\\{[sdf]\\}", "%" + i + "$s");
    i++;
}

I'm getting IllegalArgumentException: Illegal group reference while executing replaceFirst. Why?

almightyGOSU
  • 3,731
  • 6
  • 31
  • 41
Jacek Kwiecień
  • 12,397
  • 20
  • 85
  • 157
  • 1
    for every match, it would do the replace. Then what's the need for replaceFirst? – Avinash Raj Jun 07 '15 at 11:02
  • 1
    Based on your code you should probably be using `matcher.appendReplacement` examples: http://stackoverflow.com/a/19645633/1393766 http://stackoverflow.com/a/25081783/1393766 – Pshemo Jun 07 '15 at 11:03

4 Answers4

5

replacement part in replaceFirst(regex,replacement) can contain references to groups matched by regex. To do this it is using

  • $x syntax where x is integer representing group number,
  • ${name} where name is name of named group (?<name>...)

Because of this ability $ is treated as special character in replacement, so if you want to make $ literal you need to

  • escape it with \ like replaceFirst(regex,"\\$whatever")
  • or let Matcher escape it for you using Matcher.quote method replaceFirst(regex,Matcher.quote("$whatever"))

BUT you shouldn't be using

value = value.replaceFirst("\\{[sdf]\\}", "%" + i + "\\$s");

inside loop because each time you do, you need to traverse entire string to find part you want to replace, so each time you need to start from beginning which is very inefficient.

Regex engine have solution for this inefficiency in form of matcher.appendReplacement(StringBuffer, replacement) and matcher.appendTail(StringBuffer).

  • appendReplacement method is adding to StringBuffer all data until current match, and lets you specify what should be put in place of matched by regex part
  • appendTail adds part which exists after last matched part

So your code should look more like

StringBuffer sb = new StringBuffer();
int i = 0;

Matcher tagsMatcher = Pattern.compile("\\{[sdf]\\}").matcher(value);
while (tagsMatcher.find()) {
    tagsMatcher.appendReplacement(sb, Matcher.quoteReplacement("%" + (i++) + "$s"));
} 
value = sb.toString();
Pshemo
  • 122,468
  • 25
  • 185
  • 269
3

You need to escape the dollar symbol.

value = value.replaceFirst("\\{[sdf]\\}", "%" + i + "\\$s");

Illegal group reference error occurs mainly because of trying to refer a group which really won't exists.

Avinash Raj
  • 172,303
  • 28
  • 230
  • 274
3

Special character $ can be handled is simple way. Check below example

public static void main(String args[]){
        String test ="Other company in $ city ";
        String test2 ="This is test company ";
        try{
            test2= test2.replaceFirst(java.util.regex.Pattern.quote("test"),  Matcher.quoteReplacement(test));
            System.out.println(test2);
            test2= test2.replaceAll(java.util.regex.Pattern.quote("test"),  Matcher.quoteReplacement(test));
            System.out.println(test2);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

Output:

This is Other company in $ city  company 
This is Other company in $ city  company 
MBR
  • 287
  • 1
  • 4
  • 15
0

I solved it by using apache commons, org.apache.commons.lang3.StringUtils.replaceOnce. This is regex safe.

Kannan Ramamoorthy
  • 3,980
  • 9
  • 45
  • 63