web-dev-qa-db-fra.com

String.replaceAll simple barre oblique inverse avec double barre oblique inverse

J'essaie de convertir le String\something\ dans le String\\something\\ en utilisant replaceAll, mais je continue à avoir toutes sortes d’erreurs. Je pensais que c'était la solution:

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

Mais cela donne l'exception ci-dessous:

Java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
110
Frank Groeneveld

Le String#replaceAll() interprète l'argument comme un expression régulière . Le \ Est un caractère d'échappement entre et les deux String et regex. Vous devez double-y échapper pour regex:

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

Mais vous n’avez pas nécessairement besoin de regex pour cela, simplement parce que vous voulez un remplacement exact caractère par caractère et que vous n’avez pas besoin de motifs ici. Donc String#replace() devrait suffire:

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

Update : selon les commentaires, vous semblez vouloir utiliser la chaîne dans un contexte JavaScript. Vous feriez peut-être mieux d'utiliser StringEscapeUtils#escapeEcmaScript() pour couvrir plus de caractères.

189
BalusC

Pour éviter ce genre de problème, vous pouvez utiliser replace (qui prend une chaîne simple) au lieu de replaceAll (qui prend une expression régulière). Vous aurez toujours besoin d'échapper aux barres obliques inverses, mais pas de la manière sauvage requise avec les expressions régulières.

14
Fabian Steeg

TLDR: utilisez theString = theString.replace("\\", "\\\\"); à la place.


Problème

replaceAll(target, replacement) utilise la syntaxe d'expression régulière (regex) pour target et partiellement pour replacement.

Le problème est que \ est un caractère spécial dans regex (il peut être utilisé comme \d pour représenter digit) et en littéral chaîne (il peut être utilisé comme "\n" pour représenter un séparateur de ligne ou \" pour échapper au symbole de guillemet double qui représenterait normalement la fin de chaîne littérale. ).

Dans les deux cas, pour créer le symbole \, nous pouvons l'échapper le (le rendre littéral au lieu d'un caractère spécial) en plaçant un \ supplémentaire devant celui-ci (comme nous échappons le " dans les littéraux de chaîne via \").

Donc, pour target regex représentant le symbole \ devra contenir \\, et la chaîne de caractères représentant ce texte devra ressembler à "\\\\".

Nous avons donc échappé deux fois à \:

  • une fois dans regex \\
  • une fois dans le littéral de chaîne "\\\\" (chaque \ est représenté par "\\").

Dans le cas de replacement\ est également spécial. Il nous permet d’échapper à un autre caractère spécial $ qui, via la notation $x, nous permet d’utiliser une partie des données mises en correspondance par regex et conservées par un groupe de capture indexé sous la forme x, comme "012".replaceAll("(\\d)", "$1$1"), correspondant à chaque digit, placez-le dans le groupe de capture 1 et $1$1 le remplacera par ses deux copies (il le dupliquera), résultant en "001122".

Encore une fois, pour que replacement représente \ littéral, nous devons y échapper avec un \ supplémentaire, ce qui signifie que:

  • le remplacement doit contenir deux barres obliques inverses \\
  • et le littéral de chaîne qui représente \\ ressemble à "\\\\"

MAIS puisque nous voulons que replacement tienne deux barres obliques inverses, nous avons besoin de "\\\\\\\\" (chaque \ représenté par un "\\\\").

Donc, la version avec replaceAll peut ressembler à

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

Manière plus facile

Pour rendre la vie plus facile Java fournit des outils pour échapper automatiquement du texte dans les parties target et replacement. Nous pouvons maintenant nous concentrer uniquement sur les chaînes et oublier à propos de la syntaxe des expressions rationnelles:

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

qui dans notre cas peut ressembler à

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

Encore mieux

Si nous n’avons pas vraiment besoin du support de la syntaxe regex, n'impliquons pas du tout replaceAll. À la place, utilisons replace. Les deux méthodes remplaceront all targets, mais replace ne comporte pas syntaxe de regex. Pour que vous puissiez simplement écrire

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

Vous aurez besoin d'échapper à la barre oblique inverse (échappée) dans le premier argument car il s'agit d'une expression régulière. Remplacement (2ème argument - voir Matcher # replaceAll (String) ) a également sa signification particulière de barres obliques inverses. Vous devrez donc remplacer celles-ci par:

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

Oui ... au moment où le compilateur regex voit le motif que vous lui avez donné, il ne voit plus qu'une seule barre oblique inverse (car le lexer de Java a transformé le double backwhack en un seul). Vous devez remplacer "\\\\" avec "\\\\", Croyez-le ou non! Java a vraiment besoin d'une bonne syntaxe de chaîne brute.

4