String.replace - все одиночные обратные косые черты с двойными обратными косыми чертами


122

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

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

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

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

Ответы:


205

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

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

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

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

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


Фактически, он используется в AST JavaScript, который должен быть преобразован обратно в исходный код. Ваше решение работает. Спасибо!
Фрэнк Гроеневельд,

2
Если вы String#replaceAll()все равно хотите использовать , вы можете указать строку замены с помощью Matcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse

Matcher.quoteReplacement (...) - хороший способ! См. Ответ Пшемо!
Хартмут П.

14

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


10

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


проблема

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

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

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

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

Так мы \дважды сбежали :

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

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

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

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

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

Таким образом, версия с replaceAllможет выглядеть как

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

Более простой способ

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

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

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

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

Даже лучше

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

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

7

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

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

3

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.