85

Is there any way to replace a regexp with modified content of capture group?

Example:

Pattern regex = Pattern.compile("(\\d{1,2})");
Matcher regexMatcher = regex.matcher(text);
resultString = regexMatcher.replaceAll("$1"); // *3 ??

And I'd like to replace all occurrence with $1 multiplied by 3.

edit:

Looks like, something's wrong :(

If I use

Pattern regex = Pattern.compile("(\\d{1,2})");
Matcher regexMatcher = regex.matcher("12 54 1 65");
try {
    String resultString = regexMatcher.replaceAll(regexMatcher.group(1));
} catch (Exception e) {
    e.printStackTrace();
}

It throws an IllegalStateException: No match found

But

Pattern regex = Pattern.compile("(\\d{1,2})");
Matcher regexMatcher = regex.matcher("12 54 1 65");
try {
    String resultString = regexMatcher.replaceAll("$1");
} catch (Exception e) {
    e.printStackTrace();
}

works fine, but I can't change the $1 :(

edit:

Now, it's working :)

user
  • 6,567
  • 18
  • 58
  • 85

4 Answers4

84

How about:

if (regexMatcher.find()) {
    resultString = regexMatcher.replaceAll(
            String.valueOf(3 * Integer.parseInt(regexMatcher.group(1))));
}

To get the first match, use #find(). After that, you can use #group(1) to refer to this first match, and replace all matches by the first maches value multiplied by 3.

And in case you want to replace each match with that match's value multiplied by 3:

    Pattern p = Pattern.compile("(\\d{1,2})");
    Matcher m = p.matcher("12 54 1 65");
    StringBuffer s = new StringBuffer();
    while (m.find())
        m.appendReplacement(s, String.valueOf(3 * Integer.parseInt(m.group(1))));
    System.out.println(s.toString());

You may want to look through Matcher's documentation, where this and a lot more stuff is covered in detail.

earl
  • 40,327
  • 6
  • 58
  • 59
  • 10
    yes but you lose the original string. if it is "a 1 2 3 " you won't have the a – Jasper Floor Apr 13 '11 at 14:55
  • 22
    This was pretty helpful! One addition is that if your matching a text and your matches are not at the end, you would need to call m.appendTail(s); – mezzie Jul 13 '11 at 16:23
  • Be careful when finding groups with static text around like Pattern.compile("var myVar=\"(.*)\";"); - It will replace all found not only the group. see also the link of John O. – Manuel Manhart Apr 18 '14 at 13:06
  • @ManuelM.You can easily omit that problem by using positive look-ahead and look-behind assertions instead of static text. That way those parts will not get replaced by the matcher. – klaar Oct 17 '16 at 07:44
  • Thanks for the great answer, you snipped out the most useful part of that value multiplier example! This made it very clear to me! @mezzie Very good addition! Although it is present in the Java docs for `java.util.regex.Matcher`, for the folks who solely refer this SO post, it will be very helpful. :) – varun Aug 30 '19 at 18:10
11

earl's answer gives you the solution, but I thought I'd add what the problem is that's causing your IllegalStateException. You're calling group(1) without having first called a matching operation (such as find()). This isn't needed if you're just using $1 since the replaceAll() is the matching operation.

Draemon
  • 33,955
  • 16
  • 77
  • 104
11

Java 9 offers a Matcher.replaceAll() that accepts a replacement function:

resultString = regexMatcher.replaceAll(
        m -> String.valueOf(Integer.parseInt(m.group()) * 3));
shmosel
  • 49,289
  • 6
  • 73
  • 138
1

Source: java-implementation-of-rubys-gsub

Usage:

// Rewrite an ancient unit of length in SI units.
String result = new Rewriter("([0-9]+(\\.[0-9]+)?)[- ]?(inch(es)?)") {
    public String replacement() {
        float inches = Float.parseFloat(group(1));
        return Float.toString(2.54f * inches) + " cm";
    }
}.rewrite("a 17 inch display");
System.out.println(result);

// The "Searching and Replacing with Non-Constant Values Using a
// Regular Expression" example from the Java Almanac.
result = new Rewriter("([a-zA-Z]+[0-9]+)") {
    public String replacement() {
        return group(1).toUpperCase();
    }
}.rewrite("ab12 cd efg34");
System.out.println(result);

Implementation (redesigned):

import static java.lang.String.format;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class Rewriter {
    private Pattern pattern;
    private Matcher matcher;

    public Rewriter(String regularExpression) {
        this.pattern = Pattern.compile(regularExpression);
    }

    public String group(int i) {
        return matcher.group(i);
    }

    public abstract String replacement() throws Exception;

    public String rewrite(CharSequence original) {
        return rewrite(original, new StringBuffer(original.length())).toString();
    }

    public StringBuffer rewrite(CharSequence original, StringBuffer destination) {
        try {
            this.matcher = pattern.matcher(original);
            while (matcher.find()) {
                matcher.appendReplacement(destination, "");
                destination.append(replacement());
            }
            matcher.appendTail(destination);
            return destination;
        } catch (Exception e) {
            throw new RuntimeException("Cannot rewrite " + toString(), e);
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(pattern.pattern());
        for (int i = 0; i <= matcher.groupCount(); i++)
            sb.append(format("\n\t(%s) - %s", i, group(i)));
        return sb.toString();
    }
}
Mike
  • 20,010
  • 25
  • 97
  • 140