Java Regex Capturing Groups

171

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

Насколько я понимаю, это любой символ (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");
        }
    }

}
Xivilai
источник
1
Чтобы вставить новую строку, поместите 2 пробела в конце строки. Подробнее о синтаксисе уценки: en.wikipedia.org/wiki/Markdown - см. Также: stackoverflow.com/editing-help
assylias,

Ответы:

249

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

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

Что касается 3-й группы, она будет соответствовать чему-либо после последней цифры.

Если вы измените его с неохотой квантификатор в вашей 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 хранящей 0, потому что вся строка была занята жадным квантификатором, который затем возвращался, пока не вступил в контакт с одним или несколькими числами. 0 удовлетворил это, поэтому выражение удалось. Я нахожу третью группу сбивающей с толку: этот жадный квантификатор также потребляет всю строку, но отступает, пока не найдет одно или несколько чисел (\\ d +), которые должны предшествовать ему?
Xivilai
@Xivilai позвольте мне уточнить мое объяснение в моем ответе, всего на секунду.
Мена
Это хорошее объяснение. Таким образом, неохотно начинается слева и просто берет минимум, в то время как с жадным, это займет как можно больше (начиная справа), останавливаясь только перед последней цифрой, чтобы удовлетворить это условие. Третья группа берет на себя отдых.
Xivilai
@Xivilai более или менее. Это всегда начинается слева, хотя в этом случае. Вот еще немного информации о квантификаторах.
Мена
2
Вы можете использовать именованные группы захвата в Java 5/6 с named-regexp.
16

Это совершенно нормально.

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

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

F1sh
источник
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 отправляет всю строку.

Майкл Лаффарг
источник
3

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

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

Однако я не уверен, каково было первоначальное намерение автора.

FGE
источник