2011-12-22 13 views
38

Qualcuno può dirmi perchéString.replaceAll (regex) fa la stessa sostituzione due volte

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

Risultati in

aa 

Nota che il seguente ha lo stesso risultato:

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

L'ho provato su java 6 & 7 ed entrambi sembrano comportarsi nello stesso modo. Mi manca qualcosa o si tratta di un bug nel motore regex di Java?

risposta

57

Questa non è un'anomalia: .* può corrispondere a qualsiasi cosa.

vi chiediamo di sostituire tutte le ricorrenze:

  • la prima occorrenza fa corrispondere l'intera stringa, il motore regex inizia quindi dalla fine dell'input per la prossima partita;
  • ma .* corrisponde anche a una stringa vuota! Corrisponde quindi a una stringa vuota alla fine dell'input e la sostituisce con a.

L'utilizzo di .+ invece non presenta questo problema poiché questa espressione regolare non può corrispondere a una stringa vuota (richiede almeno un carattere per la corrispondenza).

In alternativa, utilizzare .replaceFirst() per sostituire solo la prima occorrenza:

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

Ora, perché .* si comporta come lo fa e non corrisponde più di due volte (teoricamente potrebbe) è una cosa interessante da prendere in considerazione . 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 @ A.H. note 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 corrispondenza.

+0

concordato. Questo vale anche per Perl. 'perl -le '$ x =" test "; $ x = ~ s /.*/ a/g; stampa $ x'' produce "aa". –

+7

@ChrisDolan: 'sed' produce solo' a', ma dubito che sia un bug. :-) –

+0

@ A.H. Sì, in effetti ... Devo leggere di nuovo "Mastering Regular Expressions" – fge

Problemi correlati