Java Regex-Erfassungsgruppen

170

Ich versuche diesen Codeblock zu verstehen. Nach was suchen wir im ersten Ausdruck?

Mein Verständnis ist, dass es sich um ein beliebiges Zeichen (0 oder mehrmals *) handelt, gefolgt von einer beliebigen Zahl zwischen 0 und 9 (ein oder mehrere Male +), gefolgt von einem beliebigen Zeichen (0 oder mehr Mal *).

Wenn dies ausgeführt wird, ist das Ergebnis:

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

Könnte jemand das bitte mit mir durchgehen?

Was ist der Vorteil der Verwendung von Erfassungsgruppen?

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
quelle
1
Um eine neue Zeile einzufügen, platzieren Sie 2 Leerzeichen am Ende der Zeile. Weitere Informationen zur Markdown-Syntax: en.wikipedia.org/wiki/Markdown - Siehe auch: stackoverflow.com/editing-help
assylias

Antworten:

248

Das Problem, das Sie haben, ist die Art des Quantifizierers. Sie verwenden einen gierigen Quantifizierer in Ihrer ersten Gruppe (Index 1 - Index 0 repräsentiert das Ganze Pattern), was bedeutet, dass er so gut wie möglich übereinstimmt (und da es sich um ein beliebiges Zeichen handelt, entspricht es so vielen Zeichen wie möglich um die Bedingung für die nächsten Gruppen zu erfüllen).

Kurz gesagt, Ihre 1. Gruppe .*stimmt mit allem überein, solange die nächste Gruppe \\d+mit etwas übereinstimmt (in diesem Fall mit der letzten Ziffer).

Gemäß der 3. Gruppe stimmt alles mit der letzten Ziffer überein.

Wenn Sie es in Ihrer ersten Gruppe in einen widerstrebenden Quantifizierer ändern , erhalten Sie das Ergebnis, das Sie vermutlich erwarten, dh den 3000- Teil.

Beachten Sie das Fragezeichen in der 1. Gruppe.

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

Ausgabe:

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

Weitere Infos zu Java Pattern hier .

Schließlich werden die Erfassungsgruppen durch runde Klammern begrenzt und bieten eine sehr nützliche Möglichkeit, Rückverweise (unter anderem) zu verwenden, sobald Sie Patternmit der Eingabe übereinstimmen.

In Java 6 können Gruppen nur nach ihrer Reihenfolge referenziert werden (Vorsicht vor verschachtelten Gruppen und der Feinheit der Reihenfolge).

In Java 7 ist dies viel einfacher, da Sie benannte Gruppen verwenden können.

Mena
quelle
Vielen Dank! Ist der Grund, warum Gruppe 2 0 gespeichert hat, weil die gesamte Zeile vom gierigen Quantifizierer verbraucht wurde, der sich dann zurückzog, bis er mit einer oder mehreren Zahlen in Kontakt kam. 0 hat dies erfüllt, sodass der Ausdruck erfolgreich war. Ich finde die dritte Gruppe verwirrend. Verbraucht dieser gierige Quantifizierer auch die gesamte Zeile, zieht sich aber zurück, bis er die eine oder die mehreren Zahlen (\\ d +) findet, die davor stehen sollen?
Xivilai
@Xivilai lässt mich meine Erklärung in meiner Antwort verfeinern, nur eine Sekunde.
Mena
Das ist eine gute Erklärung. Der Widerstrebende beginnt also von links und nimmt nur das Minimum, während er bei den Gierigen so viel wie möglich benötigt (von rechts beginnend) und erst vor der letzten Ziffer anhält, um diese Bedingung zu erfüllen. Die dritte Gruppe übernimmt den Rest.
Xivilai
@ Zivilai mehr oder weniger. In diesem Fall beginnt es jedoch immer von links. Hier finden Sie weitere Informationen zu Quantifizierern.
Mena
2
Sie können benannte Erfassungsgruppen in Java 5/6 mit verwenden named-regexp.
16

Das ist völlig in Ordnung.

  1. Die erste Gruppe ( m.group(0)) erfasst immer den gesamten Bereich, der von Ihrem regulären Ausdruck abgedeckt wird . In diesem Fall ist es die gesamte Zeichenfolge.
  2. Reguläre Ausdrücke sind standardmäßig gierig, was bedeutet, dass die erste Gruppe so viel wie möglich erfasst, ohne die Regex zu verletzen. Der (.*)(\\d+)(der erste Teil Ihres regulären Ausdrucks) deckt den ...QT300int der ersten Gruppe und den 0in der zweiten ab.
  3. Sie können dies schnell beheben, indem Sie die erste Gruppe nicht gierig machen: Wechseln Sie (.*)zu (.*?).

Weitere Informationen zu gierig oder faul finden Sie auf dieser Website.

f1sh
quelle
4

Aus dem Dokument:

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

Erfassen Sie also Gruppe 0 und senden Sie die gesamte Zeile.

Michael Laffargue
quelle
3

Dein Verständnis ist korrekt. Wenn wir jedoch durchgehen:

  • (.*) wird die ganze Saite schlucken;
  • es muss Zeichen zurückgeben, damit (\\d+)es zufrieden ist (weshalb 0es erfasst wird und nicht 3000);
  • Der letzte (.*)erfasst dann den Rest.

Ich bin mir jedoch nicht sicher, was die ursprüngliche Absicht des Autors war.

fge
quelle