Java replaceAll() File.separator

When dealing with file paths, it's not uncommon to construct a path where the file separators are missing or duplicated. Ie, instead of c:\temp\test.txt, you end up with c:\temp\\test.txt, etc. The real solution I suppose is to be meticulous about constructing the path, but in this case I found myself implementing a method to strip duplicate separators.

Using RegexBuddy, I came up with a regex to match duplicate Windows and/or Unix file separators, unless they are at the start of the string. Ie, in Windows, \\server\share\file is a valid path, not a duplicate separator. My solution uses negative look-behind.

 protected static String stripRedundantSeparators(String path) {
  return path.replaceAll("(?<!^)(\\\\|/){2,}", File.separator);
 }

However, my unit test threw a StringIndexOutOfBoundsException. Messing around with it, I came up with the following working code.

 protected static String stripRedundantSeparators(String path) {
  return path.replaceAll("(?<!^)(\\\\|/){2,}", "@").replace("@".charAt(0), File.separatorChar);
 }

What I found was that File.separator was an invalid replacement string in the regular expression version or replaceAll(), but not in the regular string or char versions of replace(). Looking at the documentation for replaceAll() revealed the problem.

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; see Matcher.replaceAll. Use Matcher.quoteReplacement(java.lang.String) to suppress the special meaning of these characters, if desired.
Note: this is only documented as of Java 1.6+.

Well, what a waste of 30 minutes. Here is the final code.

 protected static String stripRedundantSeparators(String path) {
  return path.replaceAll("(?<!^)(\\\\|/){2,}", Matcher.quoteReplacement(File.separator));
 }


I'm currently working at NerdWallet, a startup in San Francisco trying to bring clarity to all of life's financial decisions. We're hiring like crazy. Hit me up on Twitter, I would love to talk.

Follow @chase_seibert on Twitter