String.replaceВсі поодинокі риски з подвійним нахилом


122

Я намагаюся перетворити String \something\в String \\something\\користувальний replaceAll, але я все одно отримую всілякі помилки. Я думав, що це рішення:

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

Але це дає наступний виняток:

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

Відповіді:


204

String#replaceAll()Інтерпретує аргумент як регулярний вираз . \Є маскуючий в обох String і regex. Вам потрібно подвоїти його для 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

TLDR: використовувати theString = theString.replace("\\", "\\\\");замість цього.


Проблема

replaceAll(target, replacement)використовує синтаксис регулярного вираження (регулярного вираження) для targetі частково для replacement.

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

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

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

Тож ми втекли \двічі:

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

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

Отже, знову для того, щоб replacementпредставляти \буквальне, нам потрібно уникнути його з додатковими, \що означає:

  • заміна повинна містити два символи зворотної косої риски \\
  • і Stral literal, який представляє, \\виглядає так"\\\\"

АЛЕ, оскільки ми хочемо 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 # substituAll (String) ) також має особливе значення зворотних нахилів , тому вам доведеться замінити їх на:

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

3

Так ... до моменту, коли компілятор регулярних виразів побачить шаблон, який ви йому надали, він бачить лише одну зворотну косу рису (оскільки lexer Java перетворив подвійний зворотний хід у єдиний). Вам необхідно замінити "\\\\"з "\\\\", вірити цьому чи ні! Java справді потребує хорошого синтаксису сирого рядка.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.