En Java, et il semble dans quelques autres langages, les références arrières dans le modèle sont précédées d'une barre oblique inverse (par exemple \1
, \2
, \3
, etc.), mais dans une chaîne de remplacement, ils sont précédés d'un signe dollar (par exemple $1
, $2
, $3
, et aussi $0
).
Voici un extrait pour illustrer:
System.out.println(
"left-right".replaceAll("(.*)-(.*)", "\\2-\\1") // WRONG!!!
); // prints "2-1"
System.out.println(
"left-right".replaceAll("(.*)-(.*)", "$2-$1") // CORRECT!
); // prints "right-left"
System.out.println(
"You want million dollar?!?".replaceAll("(\\w*) dollar", "US\\$ $1")
); // prints "You want US$ million?!?"
System.out.println(
"You want million dollar?!?".replaceAll("(\\w*) dollar", "US$ \\1")
); // throws IllegalArgumentException: Illegal group reference
Des questions:
$
pour les références arrières dans les chaînes de remplacement propres à Java? Sinon, quelle langue a commencé? Quelles saveurs l'utilisent et qu'est-ce qui ne fonctionne pas?L'utilisation de $ for backreferences dans les chaînes de remplacement est-elle propre à Java?
Non. Perl l'utilise et Perl est certainement antérieur à la classe Pattern
de Java. La prise en charge des expressions régulières Java est explicitement décrite en termes d'expressions régulières Perl.
Par exemple: http://perldoc.Perl.org/perlrequick.html#Search-and-replace
Pourquoi est-ce une bonne idée?
Et bien évidemment vous ne pensez pas que ce soit une bonne idée! Mais l'une des raisons pour lesquelles c'est une bonne idée est de rendre le support Java recherche/remplacement (plus) compatible avec Perl).
Il existe une autre raison pour laquelle $
aurait pu être considéré comme un meilleur choix que \
. C'est que \
doit être écrit comme \\
dans un Java chaîne littérale.
Mais tout cela n'est que pure spéculation. Aucun de nous n'était dans la pièce lorsque les décisions de conception ont été prises. Et finalement, peu importe pourquoi ils ont conçu la syntaxe de remplacement de cette façon. Les décisions ont été prises et concrétisées, et toute discussion ultérieure est purement académique ... à moins que vous ne soyez en train de concevoir un nouveau langage ou une nouvelle bibliothèque regex pour Java.
Après avoir fait quelques recherches, j'ai compris les problèmes maintenant: Perl avait pour utiliser un symbole différent pour les références arrières de modèle et les références arrières de remplacement, et tandis que Java.util.regex.*
n'a pas à suivre, il choisit de le faire, non pour une raison technique mais plutôt traditionnelle.
(Veuillez garder à l'esprit que tout ce que je sais à propos de Perl à ce stade vient de la lecture d'articles sur Wikipédia, alors n'hésitez pas à corriger les erreurs que j'ai pu commettre)
La raison pour laquelle devait être fait de cette façon en Perl est la suivante:
$
comme un sceau (c'est-à-dire un symbole attaché au nom de la variable).$1
, $2
, etc.Ainsi, en raison de la façon dont Perl est interprété et du fonctionnement de son moteur d'expression rationnelle, une barre oblique précédente pour les références arrières (par exemple \1
) dans le modèle doit être utilisé, car si le sceau $
est utilisé à la place (par exemple $1
), cela provoquerait une interpolation de variable involontaire dans le motif.
La chaîne de remplacement, en raison de son fonctionnement en Perl, est évaluée dans le contexte de chaque correspondance. Il est tout à fait naturel pour Perl d'utiliser ici l'interpolation de variables, donc le moteur d'expression régulière capture des groupes dans des variables $1
, $2
, etc., pour que cela fonctionne de manière transparente avec le reste de la langue.
Java est un langage très différent de Perl, mais le plus important ici est qu'il n'y a pas d'interpolation variable. De plus, replaceAll
est un appel de méthode, et comme pour tous les appels de méthode en Java, les arguments sont évalués une fois, avant la méthode invoquée.
Ainsi, la fonction d'interpolation variable en elle-même n'est pas suffisante, car en substance, la chaîne de remplacement doit être réévaluée à chaque correspondance, et ce n'est tout simplement pas la sémantique des appels de méthode en Java. Une chaîne de remplacement à interpolation variable évaluée avant le replaceAll
est même invoqué est pratiquement inutile; l'interpolation doit se produire pendant la méthode, à chaque match.
Puisque ce n'est pas la sémantique de Java, replaceAll
doit faire cette interpolation "juste à temps" manuellement. En tant que tel, il n'y a absolument aucune raison technique pour laquelle $
est le symbole d'échappement pour les références arrières dans les chaînes de remplacement. Cela aurait très bien pu être le \
. Inversement, des références inverses dans le modèle auraient également pu être évitées avec $
au lieu de \
, et ça aurait toujours fonctionné aussi bien techniquement.
La raison Java ne regex la façon dont il le fait est purement traditionnelle: elle suit simplement le précédent établi par Perl.