Pourquoi Chaîne.replaceAll() en java nécessite 4 slash "\\" dans les regex pour remplacer ""?

j'ai remarqué récemment, String.replaceAll (regex,replacement) se comporte très bizarrement quand il s'agit du caractère d'évasion ""(slash)

par exemple considérer qu'il y a une chaîne avec filepath - String text = "E:dummypath" et nous voulons remplacer le "" avec "/" .

text.replace("","/") donne la sortie "E:/dummypath" tandis que text.replaceAll("","/") soulève l'exception java.util.regex.PatternSyntaxException .

Si nous voulons mettre en œuvre la même fonctionnalité avec replaceAll() nous devons l'écrire comme, text.replaceAll("\","/")

une différence notable est replaceAll() a ses arguments en reg-ex tandis que replace() a des arguments caractère-séquence!

mais text.replaceAll("n","/") fonctionne exactement de la même manière que son équivalent text.replace("n","/")

Creuser Plus Profond: Des comportements encore plus bizarres peuvent être observés lorsque nous essayons d'autres entrées.

permet d'attribuer text="HellonWorldn"

Now, text.replaceAll("n","/") , text.replaceAll("n","/") , text.replaceAll("\n","/") tous ces trois donne la même sortie Hello/World/

Java avait vraiment foiré avec le reg-ex dans sa meilleure façon possible je me sens! Aucune autre langue ne semble avoir ces comportements enjoués en reg-ex. Pourquoi Java a-t-il foiré comme ça?

26
demandé sur shmosel 2013-09-18 19:03:57

6 réponses

@Pierre Lawrey réponse décrit la mécanique. Le" problème " est que Backlash est un caractère escape dans les deux littérales Java string, et dans le mini-langage de regexes. Ainsi, lorsque vous utilisez une chaîne de caractères littérale pour représenter un regex, il y a deux ensembles d'échapper à considérer ... selon ce que vous voulez de la regex.

mais pourquoi c'est comme ça?

c'est une chose historique. Java n'avait pas de regexe à l'origine. Syntaxe les règles pour la littérature Java String ont été empruntées à C / C++, qui n'avait pas non plus de support regex intégré. La maladresse de la double évasion n'est apparue en Java que lorsqu'ils ont ajouté le support regex sous la forme de la classe Pattern ... dans Java 1.4.

alors comment les autres langues parviennent-elles à éviter cela?

ils le font en fournissant un support syntaxique direct ou indirect pour regexes dans le langage de programmation lui-même . Par exemple, dans Perl, Ruby, Javascript et beaucoup d'autres langues, il y a une syntaxe pour patterns / regexs (par exemple '/pattern/') où les règles d'échappement littérales de la chaîne ne s'appliquent pas. En C# et Python, ils fournissent une syntaxe littérale alternative "brute" dans laquelle les antislashes ne sont pas des échappatoires. (Mais notez que si vous utilisez la syntaxe normale c# / Python string, Vous avez le problème Java de double Escape.)


Pourquoi faire text.replaceAll("\n","/") , text.replaceAll("\n","/") , et text.replaceAll("\\n","/") donnent tous le même résultat?

le premier cas est un caractère newline au niveau de la chaîne. Le langage regex Java traite tous les caractères non spéciaux comme s'appariant eux-mêmes.

Le deuxième cas est un antislash suivi d'un "n" à la Chaîne. Le langage Java regex interprète un antislash suivi d'un" n " comme une nouvelle ligne.

le dernier cas est un revers suivi d'une caractère newline au niveau de la chaîne. Le langage regex Java ne reconnaît pas ceci comme une séquence d'échappement (regex) spécifique. Toutefois, dans la langue regex, un antislash suivi d'un caractère non-alphabétique signifie ce dernier. Donc, un antislash suivi d'un caractère de saut de ligne ... signifie la même chose qu'un retour à la ligne.

22
répondu Stephen C 2018-03-20 01:11:11

vous avez besoin d'esacpe deux fois, une fois pour Java, une fois pour le regex.

code Java est

"\\"

fait une chaîne regex de

"\" - two chars

mais le regex a aussi besoin d'une escape donc il se transforme en

\ - one symbol
24
répondu Peter Lawrey 2013-09-18 15:07:54

1) disons que vous voulez remplacer un simple \ en utilisant la méthode replaceAll de Java:

\
˪--- 1) the final backslash

2) La méthode replaceAll de Java prend un regex comme premier argument. Dans un regex literal , \ a un sens particulier, p.ex. dans \d qui est un raccourci pour [0-9] (n'importe quel chiffre). La façon d'échapper à un metachar dans un regex littéral est de le précéder d'un \ , qui conduit à:

\
|˪--- 1) the final backslash
˪---- 2) the backslash needed to escape 1) in a regex literal

3) en Java, il n'y a pas de regex literal : vous écrivez un regex dans un string literal (contrairement au JavaScript, par exemple, où vous pouvez écrire /\d+/ ). Mais dans un chaîne littérale , \ a également un sens spécial, par exemple dans \n (une nouvelle ligne) ou \t (un onglet). La façon d'échapper à un metachar dans un chaîne littérale est de précédez-le d'un \ , qui mène à:

\\
|||˪--- 1) the final backslash
||˪---- 3) the backslash needed to escape 1) in a string literal
|˪----- 2) the backslash needed to escape 1) in a regex literal
˪------ 3) the backslash needed to escape 2) in a string literal
3
répondu sp00m 2018-04-30 09:36:01

C'est parce que Java essaie de donner à \ une signification spéciale dans la chaîne de remplacement, de sorte que \$ sera un signe littéral$, mais dans le processus ils semblent avoir enlevé la signification spéciale réelle de \

alors que text.replaceAll("\\","/") , au moins peut être considéré comme correct dans un certain sens( bien qu'il ne soit pas en soi tout à fait juste), les trois exécutions, text.replaceAll("\n","/") , text.replaceAll("\n","/") , text.replaceAll("\\n","/") donner la même sortie semble encore plus drôle. Il est juste contredire pourquoi ils ont limité le fonctionnement de text.replaceAll("\","/") pour la même raison.

Java n'a pas fait d'erreur avec les expressions régulières. C'est parce que, Java aime à gâcher avec des codeurs en essayant de faire quelque chose d'unique et de différent, quand il n'est pas nécessaire.

-1
répondu coder91 2013-09-18 17:09:46

une façon de contourner ce problème est de remplacer backslash avec un autre caractère, utiliser ce caractère stand-in pour les remplacements intermédiaires, puis le convertir de nouveau en backslash à la fin. Par exemple, pour convertir "\r\n" "\n":

String out = in.replace('\','@').replaceAll("@r@n","@n").replace('@','\');

bien sûr, cela ne fonctionnera pas très bien si vous choisissez un caractère de remplacement qui peut apparaître dans la chaîne de caractères.

-2
répondu MTaylorEx 2015-07-29 18:12:49

je pense que java a vraiment foiré avec l'expression régulière dans String.replaceAll ();

en dehors de java, je n'ai jamais vu une expression régulière de langage parse de cette façon. Vous serez confus si vous avez utilisé regex dans d'autres langues.

dans le cas de l'utilisation du "\" dans la chaîne de remplacement, vous pouvez utiliser java.util.regex.Matcher.quoteReplacement(String)

String.replaceAll("/", Matcher.quoteReplacement("\"));

en utilisant cette classe Matcher vous pouvez obtenir le résultat attendu.

-3
répondu Rajagopal 2016-05-17 16:35:10