String.replaceTous les contre-obliques simples avec des contre-obliques doubles

122

J'essaye de convertir le String \something\en String \\something\\utilisation replaceAll, mais j'obtiens toujours 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
Frank Groeneveld
la source

Réponses:

204

L' String#replaceAll()interprète l'argument comme une expression régulière . Le \est un caractère d'échappement dans les deux String et regex. Vous devez le double-é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 modèles ici. Cela String#replace()devrait donc suffire:

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

Mise à jour : selon les commentaires, vous semblez vouloir utiliser la chaîne dans le contexte JavaScript. Vous feriez peut-être mieux d'utiliser à la StringEscapeUtils#escapeEcmaScript()place pour couvrir plus de personnages.

BalusC
la source
En fait, il est utilisé dans un JavaScript AST qui doit être reconverti en source. Votre solution fonctionne. Merci!
Frank Groeneveld
2
Si vous voulez String#replaceAll()quand même l' utiliser , vous pouvez citer la chaîne de remplacement avec Matcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse
Matcher.quoteReplacement (...) est un bon moyen! S'il vous plaît voir la réponse de Pshemo!
Hartmut P.
14

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 devrez toujours échapper les contre-obliques, mais pas de la manière sauvage requise avec les expressions régulières.

Fabian Steeg
la source
10

TLDR: utilisez theString = theString.replace("\\", "\\\\");plutôt.


Problème

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

Le problème est qu'il \s'agit d'un caractère spécial dans l'expression régulière (il peut être utilisé comme \dpour représenter un chiffre) et dans une chaîne littérale (il peut être utilisé comme "\n"pour représenter un séparateur de ligne ou \"pour échapper un double guillemet qui normalement représenterait la fin de la chaîne littérale).

Dans ces deux cas, pour créer un \symbole, nous pouvons l' échapper (le rendre littéral au lieu d'un caractère spécial) en le plaçant \devant lui (comme nous nous échappons "dans une chaîne littérale via \").

Donc, pour targetregex représentant un \symbole, il faudra tenir \\, et une chaîne littérale représentant un tel texte devra ressembler à "\\\\".

Nous nous sommes donc échappés \deux fois:

  • une fois dans regex \\
  • une fois dans String littéral "\\\\"(chacun \est représenté par "\\").

En cas de replacement \c'est aussi spécial là-bas. Cela nous permet d'échapper à un autre caractère spécial $qui, via la $xnotation, nous permet d'utiliser une partie des données appariées par regex et détenues par le groupe de capture indexé comme x, comme "012".replaceAll("(\\d)", "$1$1")correspondra chaque chiffre, placez-le dans le groupe de capture 1 et $1$1le remplacera par ses deux copies (il le dupliquera) résultant en "001122".

Encore une fois, pour laisser replacementreprésenter \littéral, nous devons lui échapper avec un supplément, \ce qui signifie que:

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

MAIS puisque nous voulons replacementcontenir deux contre-obliques, nous aurons besoin "\\\\\\\\"(chacun \représenté par un "\\\\").

Donc la version avec replaceAllpeut ressembler à

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

Un moyen plus simple

Pour vous faciliter la vie, Java fournit des outils permettant d'échapper automatiquement le texte dans targetet dans des replacementparties. Alors maintenant, nous pouvons nous concentrer uniquement sur les chaînes et oublier la syntaxe regex:

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 replaceAlldu tout. Au lieu de cela, utilisons replace. Les deux méthodes remplaceront tous les target s, mais replacen'impliquent pas de syntaxe regex. Pour que tu puisses simplement écrire

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

Vous devrez échapper la barre oblique inverse (échappée) dans le premier argument car il s'agit d'une expression régulière. Replacement (2nd argument - see Matcher # replaceAll (String) ) a également sa signification particulière pour les barres obliques inverses, vous devrez donc les remplacer par:

theString.replaceAll("\\\\", "\\\\\\\\");
sfussenegger
la source
3

Oui ... au moment où le compilateur regex voit le modèle que vous lui avez donné, il ne voit qu'un seul backslash (puisque le lexer de Java a transformé le double backwhack en un seul). Vous devez remplacer "\\\\"par "\\\\", croyez-le ou non! Java a vraiment besoin d'une bonne syntaxe de chaîne brute.

Jonathan Feinberg
la source