String.replaceВсе одиночная обратная косая черта с двойной обратной косой чертой

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

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

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

Ответ 3

TL;DR: используйте theString = theString.replace("\\", "\\\\"); вместо.


проблема

replaceAll(target, replacement) использует синтаксис регулярного выражения (regex) для target и частично для replacement.

Проблема заключается в том, что \ является специальным символом в регулярном выражении (его можно использовать как \d для представления цифры) и в строковом литерале (его можно использовать как "\n" для представления разделителя строк или \" чтобы избежать символа двойной кавычки, который обычно представляют конец строкового литерала).

В обоих случаях для создания символа \ мы можем его избежать (сделаем его буквальным вместо специального символа), добавив дополнительные \ перед ним (например, мы убежим " в строковых литералах через \").

Поэтому для target регулярного выражения, представляющего символ \, нужно будет удерживать \\, а строковый литерал, представляющий такой текст, должен выглядеть так: "\\\\".

Таким образом, мы избежали \ дважды:

  • один раз в regex \\
  • один раз в "\\\\" (каждый \ представлен как "\\").

В случае replacement \ также является специальным. Это позволяет нам избежать другого специального символа $ который через нотацию $x позволяет нам использовать часть данных, сопоставляемую регулярным выражением, и удерживаться захватом группы, проиндексированной как x, например "012".replaceAll("(\\d)", "$1$1") будет соответствовать каждой цифре, поместить ее в группу 1 и $1$1 заменит ее двумя копиями (она будет дублировать ее), что приведет к "001122".

Итак, снова, чтобы replacement представляла \ literal, нам нужно избежать ее с помощью дополнительного \ что означает:

  • замена должна содержать два символа обратной косой черты \\
  • и строковый литерал, который представляет \\ выглядит как "\\\\"

НО, так как мы хотим, чтобы replacement удерживала две обратные "\\\\\\\\" черты, нам понадобится "\\\\\\\\" (каждый \ представлен одним "\\\\").

Так что версия с replaceAll может выглядеть так:

replaceAll("\\\\", "\\\\\\\\");

Простой способ

Чтобы упростить жизнь, Java предоставляет инструменты для автоматического удаления текста в target и replacement части. Итак, теперь мы можем сосредоточиться только на строках и забыть о синтаксисе regex:

replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))

который в нашем случае может выглядеть

replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))

Даже лучше

Если нам не нужна поддержка синтаксиса regex, вы можете вообще не включать replaceAll. Вместо этого используйте replace. Оба метода заменят все target s, но replace не включает синтаксис regex. Так что вы могли бы просто написать

theString = theString.replace("\\", "\\\\");

Ответ 4

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

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

Ответ 5

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