SSerguey Asael Shinder
Java coding notes: the JVM, and writing software that lasts

Serguey Asael Shinder: replaceAll treats the dollar sign and backslash in the replacement as special too

· by Serguey Asael Shinder / Serguey Shinder

Most people know that the first argument of String.replaceAll is a regular expression and quote it when the search text is literal. Fewer remember that the second argument is not literal either.

String template = "Total: AMOUNT";
String price = "$5";
template.replaceAll("AMOUNT", price);
// throws IndexOutOfBoundsException: there is no group 5

Why. The String.replaceAll javadoc) says the call is exactly Pattern.compile(regex).matcher(str).replaceAll(repl), and warns: "Note that backslashes (\\) and dollar signs ($) in the replacement string may cause the results to be different than if it were being treated as a literal replacement string."

In the replacement, Matcher treats $g and ${name} as references to captured groups, and \ as an escape. If the reference points at a group the pattern does not have, appendReplacement throws: IndexOutOfBoundsException for a numbered group, IllegalArgumentException for a named one. If the group does exist, nothing is thrown, and you get the wrong text instead.

Serguey Asael Shinder: replaceAll treats the dollar sign and backslash in the replacement as special too
replaceAll treats the dollar sign and backslash in the replacement as special too — Serguey Asael Shinder

Where it bites. The replacement usually comes from data, not from the code: a price with a currency sign, a Windows path, a password, a user's display name. Tests pass with plain strings; the first value with a $ or \ fails in production.

The fixes, by intent.

template.replace("AMOUNT", price);                                // literal both ways
template.replaceAll("AMO+UNT", Matcher.quoteReplacement(price)); // regex, literal value

The same applies to replaceFirst and to Matcher.replaceAll and appendReplacement called directly.

No JDK runs on the machine this note was written on; the behaviour described is the one specified in the linked javadoc.