Захватывающие группы Java Regex

Я пытаюсь понять этот блок кода. В первом, что мы ищем в выражении?

Я понимаю, что это любой символ (0 или более раз *), за которым следует любое число от 0 до 9 (один или несколько раз +), за которым следует любой символ (0 или более раз *).

Когда это выполняется, результат:

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

Может ли кто-нибудь пропустить это со мной?

В чем преимущество использования групп захвата?

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

Проблема, с которой вы сталкиваетесь, связана с типом квантификатора. Вы используете жадный квантификатор в своей первой группе (индекс 1 - индекс 0 представляет весь Pattern), что означает, что он будет соответствовать столько, сколько он может (и поскольку это любой символ, он будет соответствовать как много символов, как есть, чтобы выполнить условие для следующих групп).

Короче говоря, ваша первая группа .* соответствует чему угодно, если следующая группа \\d+ может что-то сопоставить (в данном случае последняя цифра).

В соответствии с третьей группой, она будет соответствовать любому значению после последней цифры.

Если вы измените его на неохотный квантификатор в вашей 1-й группе, вы получите результат, я полагаю, вы ожидаете, то есть 3000 частей.

Обратите внимание на знак вопроса в 1-й группе.

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

Выход:

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

Дополнительная информация о Java Pattern здесь.

Наконец, группы захвата ограничены круглыми скобками и предоставляют очень полезный способ использования обратных ссылок (между прочим), как только ваш Pattern будет соответствовать входу.

В Java 6 группы могут ссылаться только на их порядок (остерегайтесь вложенных групп и тонкости упорядочения).

В Java 7 это намного проще, так как вы можете использовать именованные группы.

Ответ 2

Это полностью нормально.

  • Первая группа (m.group(0)) всегда фиксирует всю область, на которую распространяется ваше регулярное выражение. В этом случае это целая строка.
  • Регулярные выражения по умолчанию жадные, что означает, что первая группа захватывает как можно больше, не нарушая регулярное выражение. (.*)(\\d+) (первая часть вашего регулярного выражения) охватывает ...QT300 int первую группу и 0 во второй.
  • Вы можете быстро исправить это, сделав первую группу неживой: измените (.*) на (.*?).

Для получения дополнительной информации о жадных против лени, отметьте этот сайт.

Ответ 3

Ваше понимание верное. Однако, если мы пройдем через:

  • (.*) проглотит всю строку;
  • ему нужно будет вернуть символы, чтобы (\\d+) был аттестован (вот почему 0 захвачен, а не 3000);
  • последний (.*) затем захватит остальные.

Я не уверен, что было первоначальным намерением автора.

Ответ 4

Из документа:

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

Итак, группа захвата 0 отправляет всю строку.