17

I've looked in the Java API and some common 3rd party libraries but I'm unable to find a suitable method that will do what String.replaceAll does, except for StringBuilder.

I know that with a little work, it can be done for StringBuffer, but I don't want to go down this road because StringBuffer is slower.

Does anyone know of any 3rd party utilies, or if there is a quick piece of code to implement this functionality?

Jin Kim
  • 16,562
  • 18
  • 60
  • 86

4 Answers4

13

String.replaceAll is just a convenience method for Matcher.replaceAll. Matcher is the "actual" way to use regex in Java and is allows for a lot more sophisticated use cases.

Moreover, anything that can be done with regex methods on String can be done with similar methods on a Matcher. The beauty is, that Matchers work with more than just Strings: Matchers can be obtained for any CharSequence (an interface, which is implemented by StringBuilder, StringBuffer, String and CharBuffer). So you can simply do:

import java.util.regex.*;

...

StringBuilder sb = new StringBuilder();
sb.append("This works with StringBuffers");
Pattern p = Pattern.compile("\\Buffer\\B");
Matcher m = p.matcher(sb);
System.out.println(m.replaceAll("uilder"));

Will output This works with StringBuilders.

Working demo.

Martin Ender
  • 43,427
  • 11
  • 90
  • 130
  • 2
    I'm not sure if OP is for something like this since `m.relaceAll` will internally use StringBuffer and return String with replaced data. I believe that OP wants to know is there a way to use StringBuilder instead of StringBuffer like we use it in `Matcher#appendReplacement()` but I may be wrong. OP comment would be better here. – Pshemo Jun 28 '13 at 15:24
  • @Pshemo ah okay, I didn't know that `Matcher` uses a StringBuffer internally. – Martin Ender Jun 28 '13 at 15:25
  • 3
    Basically I have the following method signature: `public void replaceAll(final StringBuilder builder, final String regex, final String replacement)`. The builder's data should be modified without having to do any reassignement. – Jin Kim Jul 05 '13 at 20:00
3

Regex does not modify a mutable CharSequence internally. Regex parses a CharSequence to return a String, where String is the result. StringBuffer is an exception as there is special handling - as for StringBuilder being CharSequence, you have to modify it with a match result.

What you can do instead:

// Class
private static final Pattern MY_PATTERN = Pattern.compile("my|regex");

{ // Method
    StringBuilder builder;
    // ...

    Matcher m = MY_PATTERN.matcher(builder);
    builder.replace(0, builder.length(), m.replaceAll("<b>$0</b>"));
}

View a test code demo!

Unihedron
  • 10,902
  • 13
  • 62
  • 72
3

I don't want to go down this road because StringBuffer is slower.

True, but with the usual premature optimization caveat, and more importantly, modern JVMs use escape analysis to remove the StringBuffer/Vector/HashTable locks in certain cases, so once that optimization happens, the performance will be roughly the same.

David Ehrmann
  • 7,366
  • 2
  • 31
  • 40
0

Apache Harmony Matcher source code seems fully reworkable to be used with StringBuilder instead of the currently used StringBuffer, just move to the different package. It seems not dragging a lot of dependences with it. Apache license that is at the start of the file may not be bad even for commercial project.

GNU Classpath code can also be reused but the license is more difficult there (you need to publish your changed version of the Matcher but probably not the rest of your code). Same about the original Sun's implementation that can be found here in the OpenJDK project.

Audrius Meškauskas
  • 20,936
  • 12
  • 75
  • 93