Grupy przechwytywania Java Regex


170

Próbuję zrozumieć ten blok kodu. W pierwszym, czego szukamy w wyrażeniu?

Rozumiem, że jest to dowolny znak (0 lub więcej razy *), po którym następuje dowolna liczba od 0 do 9 (jeden lub więcej razy +), po której następuje dowolny znak (0 lub więcej razy *).

Po wykonaniu tej czynności wynikiem jest:

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

Czy ktoś mógłby przejść przez to ze mną?

Jakie są zalety korzystania z grup przechwytywania?

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
Aby wstawić nową linię, umieść 2 spacje na końcu linii. Więcej o składni markdown: en.wikipedia.org/wiki/Markdown - Zobacz także: stackoverflow.com/editing-help
assylias

Odpowiedzi:


248

Problem dotyczy rodzaju kwantyfikatora. Używasz chciwego kwantyfikatora w swojej pierwszej grupie (indeks 1 - indeks 0 reprezentuje całość Pattern), co oznacza, że ​​będzie on pasował jak najwięcej (a ponieważ jest to dowolny znak, dopasuje tyle znaków, ile jest w celu spełnienia warunku dla następnych grup).

Krótko mówiąc, pierwsza grupa .*pasuje do wszystkiego, o ile następna grupa \\d+może coś dopasować (w tym przypadku ostatnia cyfra).

Zgodnie z trzecią grupą, dopasuje wszystko po ostatniej cyfrze.

Jeśli zmienisz to na niechętny kwantyfikator w swojej pierwszej grupie, otrzymasz wynik, jakiego oczekujesz, czyli część 3000 .

Zwróć uwagę na znak zapytania w pierwszej grupie.

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

Wynik:

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

Więcej informacji o Javie Pattern tutaj .

Wreszcie, grupy przechwytywania są oddzielone nawiasami okrągłymi i zapewniają bardzo przydatny sposób używania odwołań wstecznych (między innymi), gdy już Patterndopasujesz dane wejściowe.

W Javie 6 do grup można się odwoływać tylko poprzez ich kolejność (uwaga na zagnieżdżone grupy i subtelną kolejność).

W Javie 7 jest to znacznie łatwiejsze, ponieważ możesz używać nazwanych grup.


Dzięki! Jest powodem, dla którego grupa 2 zapisała 0, ponieważ cała linia została zużyta przez chciwy kwantyfikator, który następnie wycofał się, dopóki nie zetknął się z jedną lub większą liczbą liczb. 0 spełnił to, więc wyrażenie się powiodło. Uważam, że trzecia grupa jest myląca, czy ten chciwy kwantyfikator również zużywa całą linię, ale cofa się, dopóki nie znajdzie jednej lub więcej liczb (\\ d +), które mają ją poprzedzać?
Xivilai

@Xivilai pozwól mi doprecyzować moje wyjaśnienie w mojej odpowiedzi, tylko sekundę.
Mena

To dobre wyjaśnienie. Więc niechętny zaczyna od lewej i bierze minimum, podczas gdy chciwy zajmie tyle, ile to możliwe (zaczynając od prawej), zatrzymując się tylko przed ostatnią cyfrą, aby spełnić ten warunek. Trzecia grupa zajmuje resztę.
Xivilai

@Xivilai mniej więcej. W tym przypadku zawsze zaczyna się od lewej strony. Oto więcej informacji na temat kwantyfikatorów.
Mena

2
Możesz używać nazwanych grup przechwytywania w Javie 5/6 z named-regexp.

16

To jest całkowicie w porządku.

  1. Pierwsza grupa ( m.group(0)) zawsze obejmuje cały obszar objęty wyrażeniem regularnym . W tym przypadku jest to cały ciąg.
  2. Wyrażenia regularne są domyślnie zachłanne, co oznacza, że ​​pierwsza grupa przechwytuje jak najwięcej bez naruszania wyrażenia regularnego. (.*)(\\d+)(Pierwsza część swojej regex) obejmuje ...QT300int pierwszą grupę i 0na sekundę.
  3. Możesz szybko to naprawić, ustawiając pierwszą grupę jako niechciwą: zmień (.*)na (.*?).

Aby uzyskać więcej informacji na temat chciwych i leniwych, odwiedź tę stronę.


4

Z dokumentu:

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

Więc przechwytuj grupę 0, wyślij całą linię.


3

Twoje rozumienie jest prawidłowe. Jeśli jednak przejdziemy przez:

  • (.*) połknie cały sznurek;
  • będzie musiał oddać postacie, aby (\\d+)był usatysfakcjonowany (dlatego 0jest przechwytywany, a nie 3000);
  • ostatni (.*)pochwyci resztę.

Nie jestem jednak pewien, jaki był pierwotny zamiar autora.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.