String.replaceВсе одиночные обратные косые черты с двойной обратной косой чертой - PullRequest
109 голосов
/ 09 ноября 2009

Я пытаюсь преобразовать String \something\ в String \\something\\, используя replaceAll, но я продолжаю получать всевозможные ошибки. Я думал, что это было решение:

theString.replaceAll("\\", "\\\\");

Но это дает следующее исключение:

java.util.regex.PatternSyntaxException: Unexpected internal error near index 1

Ответы [ 5 ]

187 голосов
/ 09 ноября 2009

String#replaceAll() интерпретирует аргумент как регулярное выражение . \ является escape-символом в и String и regex. Для регулярного выражения нужно дважды убежать от него:

string.replaceAll("\\\\", "\\\\\\\\");

Но вам не обязательно для этого нужно регулярное выражение, просто потому, что вам нужна точная посимвольная замена и вам здесь не нужны шаблоны. Так что String#replace() должно хватить:

string.replace("\\", "\\\\");

Обновление : в соответствии с комментариями, вы хотите использовать строку в контексте JavaScript. Возможно, вам лучше использовать StringEscapeUtils#escapeEcmaScript(), чтобы охватить больше символов.

14 голосов
/ 09 ноября 2009

Чтобы избежать такого рода проблем, вы можете использовать replace (который принимает простую строку) вместо replaceAll (который принимает регулярное выражение). Вам все еще нужно будет избегать обратной косой черты, но не так, как требуется для регулярных выражений.

8 голосов
/ 20 октября 2016

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("\\", "\\\\");
7 голосов
/ 09 ноября 2009

Вам потребуется экранировать (экранированный) обратный слеш в первом аргументе, поскольку это регулярное выражение. Замена (2-й аргумент - см. Matcher # replaceAll (String) ) также имеет особое значение обратной косой черты, поэтому вам придется заменить ее на:

theString.replaceAll("\\\\", "\\\\\\\\");
4 голосов
/ 09 ноября 2009

Да ... к тому моменту, когда компилятор regex увидит шаблон, который вы ему дали, он увидит только одну обратную косую черту (поскольку лексер Java превратил двойную ошибку в одну). Вам нужно заменить "\\\\" на "\\\\", хотите верьте, хотите нет! Java действительно нуждается в хорошем синтаксисе необработанных строк.

...