TLDR: используйте theString = theString.replace("\\", "\\\\");
вместо.
Проблема
replaceAll(target, replacement)
использует синтаксис регулярного выражения (регулярное выражение) для target
и частично для replacement
.
Проблема в том, что \
- это специальный символ в регулярном выражении (его можно использовать как \d
для представления цифры) и в строковом литерале (его можно использовать как "\n"
для представления разделителя строк или \"
для выхода символ двойной кавычки, который обычно представляет конец строкового литерала).
В обоих этих случаях для создания символа \
мы можем экранировать его (сделать его буквальным вместо специального символа), поместив перед ним дополнительные \
(как мы экранируем "
в строковых литералах через \"
).
Таким образом, для target
регулярное выражение, представляющее символ \
, должно содержать \\
, а строковый литерал, представляющий такой текст, должен выглядеть как "\\\\"
.
Итак, мы сбежали \
дважды:
- один раз в регулярном выражении
\\
- один раз в строковом литерале
"\\\\"
(каждый \
представлен как "\\"
).
В случае replacement
\
тоже особенное там. Это позволяет нам экранировать другой специальный символ $
, который через нотацию $x
позволяет нам использовать часть данных, сопоставленных с регулярным выражением и удерживаемых группой захвата, индексированной как x
, например, "012".replaceAll("(\\d)", "$1$1")
будет соответствовать каждой цифре, поместите ее в группе захвата 1 и $1$1
заменит его двумя копиями (дублирует), что приведет к "001122"
.
Итак, еще раз, чтобы replacement
представлял \
литерал, нам нужно экранировать его дополнительным \
, что означает:
- замена должна содержать два символа обратной косой черты
\\
- и строковый литерал, представляющий
\\
, выглядит как "\\\\"
НО, поскольку мы хотим, чтобы replacement
содержал два обратных слеша, нам потребуется "\\\\\\\\"
(каждый \
, представленный одним "\\\\"
).
Так что версия с replaceAll
может выглядеть как
replaceAll("\\\\", "\\\\\\\\");
проще
Чтобы упростить жизнь, Java предоставляет инструменты для автоматического экранирования текста на части target
и replacement
. Так что теперь мы можем сосредоточиться только на строках и забыть о синтаксисе регулярных выражений:
replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))
который в нашем случае может выглядеть как
replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))
Еще лучше
Если нам действительно не нужна поддержка синтаксиса регулярных выражений, давайте вообще не задействовать replaceAll
. Вместо этого давайте использовать replace
. Оба метода заменят all target
s, но replace
не включает синтаксис регулярных выражений. Так что вы можете просто написать
theString = theString.replace("\\", "\\\\");