Gruppi di acquisizione Java Regex


171

Sto cercando di capire questo blocco di codice. Nel primo, cosa stiamo cercando nell'espressione?

La mia comprensione è che si tratta di qualsiasi carattere (0 o più volte *) seguito da qualsiasi numero compreso tra 0 e 9 (una o più volte +) seguito da qualsiasi carattere (0 o più volte *).

Quando questo viene eseguito, il risultato è:

Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT300
Found value: 0

Qualcuno potrebbe passare questo con me?

Qual è il vantaggio dell'utilizzo dei gruppi di acquisizione?

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTut3 {

    public static void main(String args[]) {
        String line = "This order was placed for QT3000! OK?"; 
        String pattern = "(.*)(\\d+)(.*)";

        // Create a Pattern object
        Pattern r = Pattern.compile(pattern);

        // Now create matcher object.
        Matcher m = r.matcher(line);

        if (m.find()) {
            System.out.println("Found value: " + m.group(0));
            System.out.println("Found value: " + m.group(1));
            System.out.println("Found value: " + m.group(2));
        } else {
            System.out.println("NO MATCH");
        }
    }

}

1
Per inserire una nuova linea, posizionare 2 spazi alla fine della linea. Ulteriori informazioni su sintassi Markdown: en.wikipedia.org/wiki/Markdown - Vedi anche: stackoverflow.com/editing-help
assylias

Risposte:


249

Il problema che stai riscontrando è con il tipo di quantificatore. Stai utilizzando un quantificatore avido nel tuo primo gruppo (indice 1 - l'indice 0 rappresenta l'intero Pattern), il che significa che corrisponderà il più possibile (e poiché è qualsiasi personaggio, abbinerà quanti più caratteri ci sono al fine di soddisfare la condizione per i gruppi successivi).

In breve, il tuo primo gruppo .*corrisponde a qualsiasi cosa, purché il gruppo successivo \\d+possa corrispondere a qualcosa (in questo caso, l'ultima cifra).

Come per il 3 ° gruppo, corrisponderà a qualsiasi cosa dopo l'ultima cifra.

Se lo cambi in un quantificatore riluttante nel tuo primo gruppo, otterrai il risultato suppongo che ti aspetti, cioè la parte 3000 .

Nota il punto interrogativo nel 1 ° gruppo.

String line = "This order was placed for QT3000! OK?";
Pattern pattern = Pattern.compile("(.*?)(\\d+)(.*)");
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
    System.out.println("group 1: " + matcher.group(1));
    System.out.println("group 2: " + matcher.group(2));
    System.out.println("group 3: " + matcher.group(3));
}

Produzione:

group 1: This order was placed for QT
group 2: 3000
group 3: ! OK?

Maggiori informazioni su Java Pattern qui .

Infine, i gruppi di acquisizione sono delimitati da parentesi tonde e forniscono un modo molto utile per utilizzare i riferimenti a ritroso (tra le altre cose), una volta che si Patternè abbinati all'input.

In Java 6 i gruppi possono essere referenziati solo dal loro ordine (attenzione ai gruppi nidificati e alla sottigliezza dell'ordine).

In Java 7 è molto più semplice, poiché puoi usare gruppi con nome.


Grazie! È il motivo per cui il gruppo 2 ha memorizzato 0 perché l'intera riga è stata consumata dal quantificatore avido che si è quindi ritirato fino a quando non è entrato in contatto con uno o più numeri. 0 soddisfatto questo, quindi l'espressione è riuscita. Trovo confuso il terzo gruppo, quel goloso quantificatore consuma anche l'intera riga, ma indietreggia fino a quando non trova l'uno o più numeri (\\ d +) che si suppone lo precedano?
Xivilai,

@Xivilai mi permetta di mettere a punto la mia spiegazione nella mia risposta, solo un secondo.
Mena,

Questa è una buona spiegazione. Quindi il riluttante parte da sinistra e prende solo il minimo mentre con l'avido ci vorrà il più possibile (a partire da destra), fermandosi solo prima dell'ultima cifra per soddisfare quella condizione. Il terzo gruppo prende il resto.
Xivilai,

@Xivilai più o meno. In ogni caso, inizia sempre da sinistra. Ecco alcune ulteriori informazioni sui quantificatori.
Mena,

2
È possibile utilizzare i gruppi di acquisizione con nome in Java 5/6 con named-regexp.

16

Questo è totalmente OK.

  1. Il primo gruppo ( m.group(0)) cattura sempre l'intera area coperta dalla tua espressione regolare . In questo caso, è l'intera stringa.
  2. Le espressioni regolari sono avide per impostazione predefinita, il che significa che il primo gruppo acquisisce il più possibile senza violare la regex. La (.*)(\\d+)(prima parte della tua regex) copre ...QT300int il primo gruppo e 0la seconda.
  3. Puoi risolverlo rapidamente rendendo il primo gruppo non avido: (.*)passa a (.*?).

Per ulteriori informazioni su avido vs pigro, controlla questo sito.


4

Dal documento:

Capturing groups</a> are indexed from left
 * to right, starting at one.  Group zero denotes the entire pattern, so
 * the expression m.group(0) is equivalent to m.group().

Quindi il gruppo di acquisizione 0 invia l'intera riga.


3

La tua comprensione è corretta. Tuttavia, se camminiamo attraverso:

  • (.*) inghiottirà l'intera corda;
  • dovrà restituire i personaggi in modo tale da (\\d+)essere saziato (motivo per cui 0viene catturato e non 3000);
  • l'ultimo (.*)catturerà quindi il resto.

Non sono sicuro di quale fosse l'intento originale dell'autore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.