Groupes de capture Java Regex

171

J'essaye de comprendre ce bloc de code. Dans le premier, que recherchons-nous dans l'expression?

Je crois comprendre qu'il s'agit de n'importe quel caractère (0 fois ou plus *) suivi de n'importe quel nombre entre 0 et 9 (une ou plusieurs fois +) suivi de n'importe quel caractère (0 fois ou plus *).

Quand ceci est exécuté, le résultat est:

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

Quelqu'un pourrait-il passer par là avec moi?

Quel est l'avantage d'utiliser des groupes de capture?

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");
        }
    }

}
Xivilai
la source
1
Pour insérer une nouvelle ligne, placez 2 espaces à la fin de la ligne. Pour en savoir plus sur la syntaxe du démarquage: en.wikipedia.org/wiki/Markdown - Voir aussi: stackoverflow.com/editing-help
assylias

Réponses:

249

Le problème que vous rencontrez concerne le type de quantificateur. Vous utilisez un quantificateur gourmand dans votre premier groupe (index 1 - index 0 représente le tout Pattern), ce qui signifie qu'il correspondra autant que possible (et comme il s'agit de n'importe quel caractère, il correspondra à autant de caractères qu'il y a afin de remplir la condition pour les groupes suivants).

En bref, votre premier groupe .*correspond à n'importe quoi tant que le groupe suivant \\d+peut correspondre à quelque chose (dans ce cas, le dernier chiffre).

Selon le 3ème groupe, il correspondra à tout ce qui se trouve après le dernier chiffre.

Si vous le changez en quantificateur réticent dans votre premier groupe, vous obtiendrez le résultat que je suppose que vous attendez, c'est-à-dire la partie 3000 .

Notez le point d' interrogation dans le 1er groupe.

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));
}

Production:

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

Plus d'informations sur Java Pattern ici .

Enfin, les groupes de capture sont délimités par des crochets et fournissent un moyen très utile d'utiliser des références arrière (entre autres), une fois que votre Patterncorrespond à l'entrée.

En Java 6, les groupes ne peuvent être référencés que par leur ordre (attention aux groupes imbriqués et à la subtilité de l'ordre).

Dans Java 7, c'est beaucoup plus facile, car vous pouvez utiliser des groupes nommés.

Mena
la source
Merci! Le groupe de raisons 2 est-il stocké à 0 parce que la ligne entière a été consommée par le quantificateur gourmand qui a ensuite reculé jusqu'à ce qu'il entre en contact avec un ou plusieurs nombres. 0 a satisfait cela et l'expression a réussi. Je trouve le troisième groupe déroutant, est-ce que ce quantificateur gourmand consomme également la ligne entière, mais recule jusqu'à ce qu'il trouve le ou les nombres (\\ d +) qui sont censés le précéder?
Xivilai
@Xivilai m'a laissé affiner mon explication dans ma réponse, juste une seconde.
Mena le
C'est une bonne explication. Donc le réticent part de la gauche et prend juste le minimum alors qu'avec le gourmand, il en faudra le plus possible (en partant de la droite), en s'arrêtant seulement avant le dernier chiffre pour satisfaire cette condition. Le troisième groupe prend le reste.
Xivilai
@Xivilai plus ou moins. Cela commence toujours par la gauche dans ce cas. Voici quelques informations supplémentaires sur les quantificateurs.
Mena le
2
Vous pouvez utiliser des groupes de capture nommés dans Java 5/6 avec named-regexp.
16

C'est tout à fait correct.

  1. Le premier groupe ( m.group(0)) capture toujours toute la zone couverte par votre expression régulière . Dans ce cas, c'est la chaîne entière.
  2. Les expressions régulières sont gourmandes par défaut, ce qui signifie que le premier groupe capture autant que possible sans violer l'expression rationnelle. Le (.*)(\\d+)(la première partie de votre regex) couvre l' ...QT300int le premier groupe et le 0dans le second.
  3. Vous pouvez rapidement résoudre ce problème en rendant le premier groupe non gourmand: passez (.*)à (.*?).

Pour plus d'informations sur gourmand ou paresseux, consultez ce site.

f1sh
la source
4

Du doc:

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().

Le groupe de capture 0 envoie donc toute la ligne.

Michael Laffargue
la source
3

Votre compréhension est correcte. Cependant, si nous parcourons:

  • (.*) avalera toute la chaîne;
  • il devra rendre des caractères pour que cela (\\d+)soit satisfait (c'est pourquoi il 0est capturé, et non 3000);
  • le dernier (.*)capturera alors le reste.

Cependant, je ne sais pas quelle était l'intention initiale de l'auteur.

fge
la source