1

Most of the cases we replace string segments using regular expression, when the replacement text is a variable, so basically it is not known by the programmer.

However we always forget, that the behavior of java matcher.replaceAll() will very much dependent on the replacement itself. Thus the replacement should not contain any '$' or '\' characters, to provide a naive result.

E.g. the following code throw "java.lang.IndexOutOfBoundsException: No group 2" in case the variable salary equals "$2".

String salary = "$2";
Pattern p = Pattern.compile("SAL");
Matcher m = p.matcher("Salary: SAL");
String s = m.replaceAll(salary);
System.out.println(s);

I know, that if '$' sign is escaped with '\', then we will get the expected result. But then again, the '\' should be escaped with '\' as well. So the proper solution would be:

String salary = "$2";
Pattern p = Pattern.compile("SAL");
Matcher m = p.matcher("Salary: SAL");
String s = m.replaceAll(salary.replace("\\", "\\\\").replace("$", "\\$"));
System.out.println(s);

Now first of all this is not so convenient to use, but also not great performance-wise. (And the same stands for the appendReplacement() method.)

So can you please recommend some more generic solution for the problem?

  • 'Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.' – cool May 27 '20 at 09:07
  • What do you mean by "not so great performance-wise"? Do you mean you don't like the two `replace` calls having to loop through the string twice? How about writing your own `escapeReplacement` method that uses a `StringBuilder` and only loops through the string once? – Sweeper May 27 '20 at 09:19
  • _performance-wise_: I introduce escape characters with two method calls, then Matcher will search for my escapes to replace them back. _not so great_: Regexp already an expensive thing, so this extra hooks might not effect the overall performance. – Balazs Kelemen May 27 '20 at 11:53

1 Answers1

0

In case if you only want to replace a specific substring with the specified literal replacement sequence, you can simply use String.replace(). Something like so:

  String source = "Salary: SAL";
  String target = "SAL";
  String salary = "$2";
  String result = source.replace(target, salary);
  System.out.println(result); // prints "Salary: $2"

It is worth noting, that it only replaces literal substring sequences and won't work if target is a regex.

Amongalen
  • 3,101
  • 14
  • 20
  • You are perfectly right in that. The example was not right. Please assume, that the pattern should be something like "S\\w*" – Balazs Kelemen May 27 '20 at 13:35
  • @BalazsKelemen Just as I expected. Going to leave it here anyway, just in case it might be useful for someone. And sadly I don't know any better way to do it for regexes. – Amongalen May 27 '20 at 13:55