String.replaceAll (regex) effettua la stessa sostituzione due volte

Qualcuno può dirmi perché?

System.out.println("test".replaceAll(".*", "a")); 

Risultati in

 aa 

Si noti che quanto segue ha lo stesso risultato:

 System.out.println("test".replaceAll(".*$", "a")); 

Ho provato questo su Java 6 e 7 ed entrambi sembrano comportarsi allo stesso modo. Mi manca qualcosa o si tratta di un bug nel motore regex di Java?

Questa non è un’anomalia:. .* Può corrispondere a qualsiasi cosa.

Chiedi di sostituire tutte le occorrenze:

  • la prima occorrenza corrisponde all’intera stringa, quindi il motore regex inizia dalla fine dell’input per la successiva corrispondenza;
  • ma .* corrisponde anche a una stringa vuota! Corrisponde quindi a una stringa vuota alla fine dell’input e la sostituisce con a .

L’uso di .+ Non mostrerà questo problema poiché questa espressione non può corrispondere a una stringa vuota (richiede almeno un carattere per corrispondere).

Oppure usa .replaceFirst() per sostituire solo la prima occorrenza:

 "test".replaceFirst(".*", "a") ^^^^^^^^^^^^ 

Ora, perché .* comporta come fa e non corrisponde più di due volte (teoricamente potrebbe) è una cosa interessante da considerare. Vedi sotto:

 # Before first run regex: |.* input: |whatever # After first run regex: .*| input: whatever| #before second run regex: |.* input: whatever| #after second run: since .* can match an empty string, it it satisfied... regex: .*| input: whatever| # However, this means the regex engine matched an empty input. # All regex engines, in this situation, will shift # one character further in the input. # So, before third run, the situation is: regex: |.* input: whatever<|ExhaustionOfInput> # Nothing can ever match here: out 

Si noti che, come le note @AH nei commenti, non tutti i motori regex si comportano in questo modo. GNU sed per esempio considererà che ha esaurito l’input dopo la prima partita.