fixing StringIndexOutOfBoundsException on replaceAll

java.lang.StringIndexOutOfBoundsException: String index out of range: 62
        at java.lang.String.charAt(String.java:686)
        at java.util.regex.Matcher.appendReplacement(Matcher.java:703)
        at java.util.regex.Matcher.replaceAll(Matcher.java:813)
        at java.lang.String.replaceAll(String.java:2189)
        at com.example.XslImportsPathFixer.fix(XslImportsPathFixer.java:50)

This “problem” on String#replaceAll can be a mind bender some times forcing you to debug for hours thinking that the regex you’ve specified (1st parameter of replaceAll) is wrong.

If you are getting the above exception then the problem lies on the replacement String (2nd parameter of replaceAll) which most probably contains $ or \.

This is mentioned in the API:

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. Dollar signs may be treated as references to captured subsequences as described above, and backslashes are used to escape literal characters in the replacement string.

So, fixing this is as easy as using Matcher.quoteReplacement(…) and in case you still use Java 1.4.2 you can do it using a helper method such as:

/**
* Escaping "$" and "\" for use as replacement values in regexes.
* 
*/
public static String quoteReplacement(String replacement) {
    return replacement.replaceAll("(\\$|\\\\)", "\\\\$0");
}   

This is especially useful in case your replacement string comes from user controlled content where you don’t know whether these special characters will exist.

This “issue” can obviously be avoided if you are first consulting the API when a JDK method seems to be misbehaving but the problem is that this was added in 1.5. So using the latest JDK (when possible) and always consulting the API is a good practice.

Comments are closed.