String.replaceAlle einzelnen Backslashes mit doppelten Backslashes

122

Ich versuche das String \something\in das String \\something\\using umzuwandeln replaceAll, aber ich bekomme immer wieder alle möglichen Fehler. Ich dachte das wäre die Lösung:

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

Dies gibt jedoch die folgende Ausnahme:

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

Antworten:

204

Das String#replaceAll()interpretiert das Argument als regulären Ausdruck . Das \ist ein Escape - Zeichen in beide String und regex. Sie müssen es für Regex doppelt entkommen:

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

Dafür benötigen Sie jedoch nicht unbedingt Regex, nur weil Sie eine genaue Ersetzung von Zeichen zu Zeichen wünschen und hier keine Muster benötigen. Also String#replace()sollte genügen:

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

Update : Gemäß den Kommentaren möchten Sie die Zeichenfolge anscheinend im JavaScript-Kontext verwenden. Sie sollten StringEscapeUtils#escapeEcmaScript()stattdessen besser verwenden , um mehr Zeichen abzudecken.

BalusC
quelle
Tatsächlich wird es in einem JavaScript-AST verwendet, das zurück in die Quelle konvertiert werden sollte. Ihre Lösung funktioniert. Vielen Dank!
Frank Groeneveld
2
Wenn Sie es String#replaceAll()trotzdem verwenden möchten, können Sie die Ersatzzeichenfolge mit Matcher # quoteReplacement () zitieren :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse
Matcher.quoteReplacement (...) ist ein guter Weg! Bitte sehen Sie Pshemos Antwort!
Hartmut P.
14

Um diese Art von Problemen zu vermeiden, können Sie replaceanstelle von replaceAll(für die ein regulärer Ausdruck verwendet wird) (die eine einfache Zeichenfolge verwendet) verwenden . Sie müssen immer noch Backslashes vermeiden, jedoch nicht auf die wilde Weise, die für reguläre Ausdrücke erforderlich ist.

Fabian Steeg
quelle
10

TLDR: Verwenden Sie theString = theString.replace("\\", "\\\\");stattdessen.


Problem

replaceAll(target, replacement)Verwendet die reguläre Ausdruckssyntax (Regex) für targetund teilweise für replacement.

Das Problem ist, dass \es sich um ein Sonderzeichen in Regex (es kann verwendet werden \d, um eine Ziffer darzustellen) und in einem Zeichenfolgenliteral (es kann verwendet werden "\n", um ein Zeilentrennzeichen darzustellen oder \"um ein doppeltes Anführungszeichen zu umgehen, das normalerweise das Ende des Zeichenfolgenliteral darstellt).

In diesen beiden Fällen erstellen \Symbol können wir entkommen sie (es anstelle von spezieller Zeichenliteral) durch zusätzliche Platzierung , \bevor es (wie wir entkommen "in Stringliterale über \").

So targetregex darstellt \Symbol zu halten brauchen \\, und Stringliteral solchen Text darstellen wird aussehen müssen "\\\\".

Also sind wir \zweimal geflohen :

  • einmal in Regex \\
  • einmal im String-Literal "\\\\"(jedes \wird als dargestellt "\\").

Im Falle von replacement \ist dort auch etwas Besonderes. Es ermöglicht uns, anderen Sonderzeichen zu entkommen , die es uns $über die $xNotation ermöglichen, einen Teil der Daten zu verwenden, die mit Regex übereinstimmen und von einer erfassten Gruppe erfasst werden x, die wie "012".replaceAll("(\\d)", "$1$1")jede Ziffer indiziert ist , sie in die Erfassungsgruppe 1 einordnet und durch $1$1ihre zwei Kopien ersetzt (es wird es duplizieren) was zu "001122".

Also noch einmal, lassen replacementrepräsentieren \wörtliche wir es mit zusätzlichen entweichen müssen , \was bedeutet , dass:

  • Das Ersetzen muss zwei Backslash-Zeichen enthalten \\
  • und String-Literal, das \\aussieht wie"\\\\"

ABER da wir zwei Backslashes replacementhalten wollen , werden wir brauchen (jeder durch einen dargestellt )."\\\\\\\\"\"\\\\"

So kann die Version mit replaceAllaussehen

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

Einfacherer Weg

Um das Leben einfacher zu machen, bietet Java Tools, mit denen Text automatisch in Teile targetund replacementTeile verschoben werden kann. Jetzt können wir uns nur auf Zeichenfolgen konzentrieren und die Regex-Syntax vergessen:

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

was in unserem Fall so aussehen kann

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

Noch besser

Wenn wir die Unterstützung der Regex-Syntax nicht wirklich brauchen, können wir sie überhaupt nicht einbeziehen replaceAll. Verwenden wir stattdessen replace. Beide Methoden ersetzen alle target s, beinhalten jedoch replacekeine Regex-Syntax. Sie könnten also einfach schreiben

theString = theString.replace("\\", "\\\\");
Pshemo
quelle
7

Sie müssen den (maskierten) Backslash im ersten Argument umgehen, da es sich um einen regulären Ausdruck handelt. Ersetzen (2. Argument - siehe Matcher # replaceAll (String) ) hat auch die besondere Bedeutung von Backslashes, daher müssen Sie diese ersetzen durch:

theString.replaceAll("\\\\", "\\\\\\\\");
sfussenegger
quelle
3

Ja ... bis der Regex-Compiler das von Ihnen angegebene Muster sieht, wird nur ein einziger Backslash angezeigt (da Javas Lexer den doppelten Backwhack in einen einzigen verwandelt hat). Sie ersetzen müssen "\\\\"mit "\\\\", es glauben oder nicht! Java braucht wirklich eine gute Raw-String-Syntax.

Jonathan Feinberg
quelle