Syntaxe des références dans les chaînes de remplacement (pourquoi signe dollar?)

En Java, et dans quelques autres langages, les backreferences dans le pattern sont précédées d’une barre oblique inverse (par exemple \1 , \2 , \3 , etc.), mais dans une chaîne de remplacement, elles sont précédées 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:

  • L’utilisation de $ pour les backreferences dans les chaînes de remplacement est-elle unique à Java? Si non, quelle langue a commencé? Quelles saveurs l’utilisent et qu’est-ce qui ne le fait pas?
  • Pourquoi est-ce une bonne idée? Pourquoi ne pas s’en tenir à la même syntaxe? Cela ne conduirait-il pas à une langue plus cohérente et plus facile à apprendre?
    • La syntaxe ne serait-elle pas plus simple si les déclarations 1 et 4 ci-dessus étaient les “correctes” au lieu de 2 et 3?

L’utilisation de $ pour les backreferences dans les chaînes de remplacement est-elle unique à Java?

Non, Perl l’utilise, et Perl est certainement antérieure à la classe Pattern de Java. Le support de regex Java est explicitement décrit en termes de regex Perl.

Par exemple: http://perldoc.perl.org/perlrequick.html#Search-and-replace

Pourquoi est-ce une bonne idée?

Bien évidemment, vous ne pensez pas que ce soit une bonne idée! Mais une des raisons pour lesquelles cela est une bonne idée est de rendre le support de recherche / remplacement Java (plus) compatible avec celui de Perl.

Il y a une autre raison possible pour laquelle $ pourrait être considéré comme un meilleur choix que \ . C’est-à-dire que \ doit être écrit en tant que \\ dans un littéral Java Ssortingng.

Mais tout cela est une pure spéculation. Aucun de nous n’était dans la pièce lorsque les décisions de conception ont été sockets. Et finalement, peu importe pourquoi ils ont conçu la syntaxe Ssortingng de remplacement de cette manière. Les décisions ont été sockets et mises en œuvre de manière concrète, et toute discussion ultérieure est purement académique… à moins que vous ne parveniez à concevoir un nouveau langage ou une nouvelle bibliothèque de regex pour Java.

Après quelques recherches, j’ai compris les problèmes maintenant: Perl devait utiliser un symbole différent pour les références de modèle et les références de remplacement, et bien que java.util.regex.* N’ait pas à suivre, il choisit, pas pour une raison technique mais plutôt traditionnelle.


Côté Perl

(S’il vous plaît gardez à l’esprit que tout ce que je sais à propos de Perl à ce stade provient de la lecture des articles Wikipedia, alors n’hésitez pas à corriger les erreurs que j’ai pu faire)

La raison pour laquelle cela devait être fait de cette manière en Perl est la suivante:

  • Perl utilise $ comme sigil (c’est-à-dire un symbole attaché au nom de la variable).
  • Les littéraux de chaîne Perl sont interpolés par variable.
  • Regex Perl capture effectivement les groupes sous la forme de variables $1 , $2 , etc.

Ainsi, en raison de la manière dont Perl est interprété et du fonctionnement de son moteur regex, une barre oblique pour les backreferences (par exemple \1 ) dans le modèle doit être utilisée, car si sigil $ est utilisé à la place (par exemple $1 ) interpolation variable 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 que Perl utilise l’interpolation des variables ici, donc le moteur regex capture les groupes dans les variables $1 , $2 , etc., pour que cela fonctionne de manière transparente avec le rest du langage.

Les références

  • Wikipedia / Ssortingng littéral – interpolation variable
  • Wikipedia / Sigil (programmation informatique)

Du côté de Java

Java est un langage très différent de Perl, mais le plus important est qu’il n’y a pas d’interpolation de variables. De plus, replaceAll est un appel de méthode et, comme avec 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 de variables n’est pas suffisante en soi, car la chaîne de remplacement doit essentiellement être réévaluée à chaque correspondance, et ce n’est pas la sémantique des appels de méthode en Java. Une chaîne de remplacement interpolée par variable qui est évaluée avant que la replaceAll soit invoquée est pratiquement inutile; L’interpolation doit avoir lieu pendant la méthode, à chaque correspondance.

Comme ce n’est pas la sémantique du langage Java, replaceAll doit effectuer cette interpolation “juste à temps” manuellement . En tant que tel, il n’y a absolument aucune raison technique pour laquelle $ est le symbole d’échappement des références dans les chaînes de remplacement. Cela aurait très bien pu être le \ . À l’inverse, les références dans le modèle pourraient également avoir été échappées avec $ au lieu de \ , et cela aurait tout de même fonctionné techniquement.

La raison pour laquelle Java fait regex comme il le fait est purement traditionnelle: il suit simplement le précédent défini par Perl.