0

I need to escape the $ and therefore I need to replace all occurrences of $ with \$

So I wrote this method:

// String#replaceAll(String regex, String replacement)
public String escape$(String str) {
    // the first \\$ to escape it in regular expression
    // the second is a normal String so \\$ should mean \$
    return str.replaceAll("\\$", "\\$");
}


String s = "$some$$text here";
System.out.println(escape$(s));

Before I submitted for production use, I thought hmmm let's test that even though I was certain it should work. And so I did...

Well you guessed it. It doesn't work! It returns the same thing!

// expected result of the above: \$some\$\$text here
// reality: $some$$text here

So why doesn't this work?!

  • what do you expect `"\\$", "\\$` to do? Find "soemthing", and then replace it with the exact SAME "something"? You need to do `\\\\$` for the replacement. – Marc B Aug 17 '16 at 15:32
  • 6
    Why are you using `replaceAll` rather than `replace`? `replaceAll` is more complex due to using regular expressions - which are causing you problems here. Just `return str.replace("$", "\\$");` should be fine... As an aside, I'd strongly recommend avoiding using `$` in your method name. From the JLS: "The $ sign should be used only in mechanically generated source code or, rarely, to access pre-existing names on legacy systems." – Jon Skeet Aug 17 '16 at 15:33
  • 1
    Why not just do `str.replace("$","\\$");`? – Zircon Aug 17 '16 at 15:33
  • 1
    @MarcB no Marc, please read the comments in the method –  Aug 17 '16 at 15:33
  • Just because your comment states something doesn't make it the case. From `Matcher.replaceAll`: "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." – Jon Skeet Aug 17 '16 at 15:34
  • http://stackoverflow.com/questions/7535317/how-to-replace-with-in-java – Wiktor Stribiżew Aug 17 '16 at 15:34
  • 1
    I didn't know that `replace` replaces all! –  Aug 17 '16 at 15:35

2 Answers2

4

You need to double-escape the replacement.

You probably don't want to use replaceAll, as you'd actually need to double-double escape it, but you're not using regular expressions here.

Instead, you can just use replace, which takes literals (and uses replaceAll in the background, with quoted values - see Matcher#quoteReplacement).

Here are two examples:

System.out.println("$".replaceAll("\\$", "\\\\\\$"));
System.out.println("$".replace("$", "\\$"));

Output

\$
\$
Mena
  • 47,782
  • 11
  • 87
  • 106
  • @DenysSéguret that would return a non-escaped `$`. – Mena Aug 17 '16 at 15:35
  • 1
    @DenysSéguret: No, it's not. Backslash and dollar still have special meaning in replacement strings. Read the docs for `Matcher.replaceAll`. – Jon Skeet Aug 17 '16 at 15:36
  • Sorry, I thought you were referring to the escapement in the first part, not the second one. My bad, I didn't read it well. – Denys Séguret Aug 17 '16 at 15:37
  • 1
    I've been programming in Java for 3 years, never thought that `replace` can actually replace all when there is a method `replaceAll` –  Aug 17 '16 at 15:39
  • 1
    @rz3r0 confusing isn't it :D – Mena Aug 17 '16 at 15:40
  • 1
    Your’re not the first. After 17 years I still have to check with the docs each time. – Ole V.V. Aug 17 '16 at 16:11
0

For Java regex, it's not just that you have to double escape a string for the language,
which always has to be done.

It's that you have to escape the dollar sign for the engine as well to
distinguish it from a capture variable.

The replace string is actually a template for a string formatter.

Always write the replacement text in it's raw form first.
The raw form is what is presented to the engine as a template for the formatter.

Raw: \\ + \$ <- the engine parses this as \ + $ (two separate literals)

Combined Raw: \\\$

Finally, for the language, just escape the escapes.

Stringed: "\\\\\\$"