java regular Sintassi di backreferenze nelle stringhe di sostituzione(perché Dollar Sign?)



regular expression creator java (2)

In Java, e sembra in alcuni altri linguaggi, i backreferences nel pattern sono preceduti da una barra rovesciata (es. \1 , \2 , \3 , ecc.), Ma in una stringa di sostituzione precedono un segno di dollaro (es. $1 , $2 , $3 e anche $0 ).

Ecco uno snippet per illustrare:

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

Domande:

  • L'uso di $ per le sottorappresentazioni nelle stringhe di sostituzione è esclusivo di Java? In caso contrario, quale lingua ha iniziato? Quali sapori lo usano e cosa no?
  • Perché è una buona idea? Perché non attenersi alla stessa sintassi di pattern? Non porterebbe a una lingua più coesa e più facile da imparare?
    • La sintassi non sarebbe più snella se le affermazioni 1 e 4 di cui sopra fossero quelle "corrette" anziché 2 e 3?

L'uso di $ per le sottorappresentazioni nelle stringhe di sostituzione è esclusivo di Java?

No. Perl lo usa, e Perl precede sicuramente la classe Pattern di Java. Il supporto di regex di Java è esplicitamente descritto in termini di regex di Perl.

Ad esempio: http://perldoc.perl.org/perlrequick.html#Search-and-replace

Perché è una buona idea?

Beh, ovviamente non pensi sia una buona idea! Ma uno dei motivi per cui è una buona idea è rendere Java search / replace support (più) compatibile con Perl's.

C'è un'altra possibile ragione per cui $ potrebbe essere stato visto come una scelta migliore di \ . Quello è che \ deve essere scritto come \\ in un letterale String Java.

Ma tutto ciò è pura speculazione. Nessuno di noi era nella stanza quando sono state prese le decisioni di progettazione. E alla fine non importa davvero perché hanno progettato la sintassi della String di sostituzione in questo modo. Le decisioni sono state prese e stabilite in concreto, e ogni ulteriore discussione è puramente accademica ... a meno che non stiate progettando una nuova lingua o una nuova libreria regex per Java.


Dopo aver fatto qualche ricerca, ho capito subito i problemi: Perl doveva usare un simbolo diverso per le backreferenze di pattern e le rimando dei riferimenti, e mentre java.util.regex.* Non doveva seguire l'esempio, sceglie, non per una ragione tecnica ma piuttosto tradizionale.

Dal lato Perl

(Tieni presente che tutto ciò che so su Perl a questo punto viene dalla lettura di articoli di Wikipedia, quindi sentiti libero di correggere eventuali errori che ho potuto commettere)

Il motivo per cui doveva essere fatto in questo modo in Perl è il seguente:

  • Perl usa $ come sigillo (cioè un simbolo collegato al nome della variabile).
  • I valori letterali stringa Perl sono variabili interpolati.
  • L'espressione regolare di Perl cattura effettivamente i gruppi come variabili $1 , $2 , ecc.

Quindi, a causa del modo in cui Perl viene interpretato e di come funziona il suo motore regex, deve essere usata una barra precedente per i riferimenti (es. \1 ) nel pattern, perché se il sigillo $ viene usato al posto (es. $1 ), causerebbe un involontario interpolazione variabile nel modello.

La stringa di sostituzione, a causa di come funziona in Perl, viene valutata nel contesto di ogni partita. È molto naturale che Perl utilizzi l'interpolazione variabile qui, quindi il motore regex cattura i gruppi in variabili $1 , $2 , ecc. Per fare in modo che tutto funzioni perfettamente con il resto della lingua.

Riferimenti

Dal lato Java

Java è una lingua molto diversa da quella di Perl, ma soprattutto qui non c'è alcuna interpolazione variabile. Inoltre, replaceAll è una chiamata al metodo, e come con tutte le chiamate di metodo in Java, gli argomenti vengono valutati una volta, prima del metodo invocato.

Pertanto, la funzione di interpolazione variabile di per sé non è sufficiente, poiché in sostanza la stringa sostitutiva deve essere rivalutata su ogni corrispondenza, e questa non è solo la semantica delle chiamate ai metodi in Java. Una stringa di sostituzione interpolata a variabili che viene valutata prima che anche il replaceAll sia invocato è praticamente inutile; l'interpolazione deve avvenire durante il metodo, in ogni partita.

Dal momento che questa non è la semantica del linguaggio Java, replaceAll deve eseguire manualmente questa interpolazione "just-in-time". In quanto tale, non vi è assolutamente alcuna ragione tecnica per cui $ è il simbolo di escape per le sottorappresentazioni nelle stringhe di sostituzione. Potrebbe essere stato molto bene il \ . Viceversa, i backreferences nel pattern potevano anche essere stati scappati con $ invece di \ , e avrebbe funzionato altrettanto bene tecnicamente.

La ragione per cui l'espressione regolare di Java è puramente tradizionale: sta semplicemente seguendo il precedente impostato da Perl.





backreference