24

I am using a while(matcher.find()) to loop through all of the matches of a Pattern. For each instance or match of that pattern it finds, I want to replace matcher.group(3) with some new text. This text will be different for each one so I am using matcher.appendReplacement() to rebuild the original string with the new changes as it goes through. However, appendReplacement() replaces the entire Pattern instead of just the group.

How can I do this but only modify the third group of the match rather than the entire Pattern?

Here is some example code:

Pattern pattern = Pattern.compile("THE (REGEX) (EXPRESSION) (WITH MULTIPLE) GROUPS");
Matcher matcher = pattern.matcher("THE TEXT TO SEARCH AND MODIFY");
StringBuffer buffer = new StringBuffer();

while(matcher.find()){
   matcher.appendReplacement(buffer, processTheGroup(matcher.group(3));
}

but I would like to do something like this (obviously this doesn't work).

...
while(matcher.find()){
   matcher.group(3).appendReplacement(buffer, processTheGroup(matcher.group(3));
}

Something like that, where it only replaces a certain group, not the whole Pattern.

EDIT: changed the regex example to show that not all of the pattern is grouped.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
cottonBallPaws
  • 21,220
  • 37
  • 123
  • 171

2 Answers2

33

I see this already has an accepted answer, but it is not fully correct. The correct answer appears to be something like this:

.appendReplacement("$1" + process(m.group(2)) + "$3");

This also illustrates that "$" is a special character in .appendReplacement. Therefore you must take care in your "process()" function to replace all "$" with "\$". Matcher.quoteReplacement(replacementString) will do this for you (thanks @Med)

The previous accepted answer will fail if either groups 1 or 3 happen to contain a "$". You'll end up with "java.lang.IllegalArgumentException: Illegal group reference"

Warren
  • 1,903
  • 1
  • 21
  • 30
  • 13
    To address this `$` issue, `Matcher.quoteReplacement( replacementString )`does the trick nicely. – Med Jan 07 '14 at 10:39
17

Let's say your entire pattern matches "(prefix)(infix)(suffix)", capturing the 3 parts into groups 1, 2 and 3 respectively. Now let's say you want to replace only group 2 (the infix), leaving the prefix and suffix intact the way they were.

Then what you do is you append what group(1) matched (unaltered), the new replacement for group(2), and what group(3) matched (unaltered), so something like this:

matcher.appendReplacement(
    buffer,
    matcher.group(1) + processTheGroup(matcher.group(2)) + matcher.group(3)
);

This will still match and replace the entire pattern, but since groups 1 and 3 are left untouched, effectively only the infix is replaced.

You should be able to adapt the same basic technique for your particular scenario.

polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • This is great thanks. Is there also a way to do this if there are some aspects of the Pattern that are not in groups? (I edited my original post's example) – cottonBallPaws Oct 15 '10 at 07:40
  • 1
    If you must include those parts in the match (i.e. you can't leave them out of the pattern), then you'd want to capture them in a group so you can remember what they matched and put them back in unaltered as part of your replacement. – polygenelubricants Oct 15 '10 at 07:59
  • Note that this will fail if one of the matched strings contains a `$`. See Warrens answer below. – Mene Oct 01 '15 at 09:25
  • Okay, what is `processTheGroup()` ?? I want to replace lets say group 2 only – Enissay Apr 02 '18 at 23:17