Что означают «ленивый» и «жадный» в контексте регулярных выражений?

370

Может ли кто-нибудь объяснить эти два термина понятным образом?

Теги:
regex-greedy
non-greedy

11 ответов

496
Лучший ответ

Жадность будет потреблять как можно больше. Из http://www.regular-expressions.info/repeat.html мы видим пример попытки сопоставления HTML-тегов с <.+>. Предположим, что у вас есть следующее:

<em>Hello World</em>

Вы можете думать, что <.+> (. означает любой символ новой строки, а + означает один или несколько) будет соответствовать только <em> и </em>, когда на самом деле это будет очень жадным, и перейти от первого < к последнему >. Это означает, что он будет соответствовать <em>Hello World</em> вместо того, что вам нужно.

Сделать это ленивым (<.+?>) предотвратит это. Добавив ? после +, мы попросим его повторить как можно меньше раз, поэтому первый >, с которым он сталкивается, - это то, где мы хотим остановить сопоставление.

Я бы посоветовал вам скачать RegExr, отличный инструмент, который поможет вам исследовать регулярные выражения - я использую его все время.

  • 2
    так что если вы используете жадность, у вас будет 3 (1 элемент + 2 тега) совпадения или только 1 совпадение (1 элемент)?
  • 9
    Это будет соответствовать только 1 раз, начиная с первого < и заканчивая последним > .
Показать ещё 7 комментариев
233

"Жадный" означает наименьшую возможную длину.

"Lazy" означает совпадение кратчайшей строки.

Например, жадный h.+l соответствует 'hell' в 'hello', но ленивый h.+?l соответствует 'hel'.

  • 70
    Отлично, ленивый остановится, как только условие l будет выполнено, но жадность означает, что он остановится только после того, как условие l больше не будет выполнено?
  • 2
    Для всех людей, читающих пост: жадные или ленивые квантификаторы сами по себе не будут соответствовать самой длинной / самой короткой из возможных подстрок. Вы должны будете использовать либо жадный жетон с умеренным темпом , либо использовать подходы без регулярных выражений.
Показать ещё 4 комментария
69
+-------------------+-----------------+------------------------------+
| Greedy quantifier | Lazy quantifier |        Description           |
+-------------------+-----------------+------------------------------+
| *                 | *?              | Star Quantifier: 0 or more   |
| +                 | +?              | Plus Quantifier: 1 or more   |
| ?                 | ??              | Optional Quantifier: 0 or 1  |
| {n}               | {n}?            | Quantifier: exactly n        |
| {n,}              | {n,}?           | Quantifier: n or more        |
| {n,m}             | {n,m}?          | Quantifier: between n and m  |
+-------------------+-----------------+------------------------------+

Добавить? к квантификатору, чтобы сделать его нелогичным, ленивым.

Пример:
тестовая строка: stackoverflow
жадное выражение reg: s.*o вывод: stackoverflo w
lazy reg выражение: s.*?o вывод: stacko verflow

  • 2
    не является ?? эквивалентно? , Точно так же, не {n}? эквивалент {n}
  • 2
    @BreakingBenjamin: нет ?? не эквивалентно?, когда у него есть выбор вернуть 0 или 1 вхождение, он выберет альтернативу 0 (ленивый). Чтобы увидеть разницу, сравните re.match('(f)?(.*)', 'food').groups() re.match('(f)??(.*)', 'food').groups() re.match('(f)?(.*)', 'food').groups() re.match('(f)??(.*)', 'food').groups() re.match('(f)?(.*)', 'food').groups() с re.match('(f)??(.*)', 'food').groups() . В последнем (f)?? не будет соответствовать ведущему 'f', даже если это возможно. Следовательно, 'f' будет сопоставляться второй группой захвата '. *'. Я уверен, что вы можете создать пример с помощью '{n}?' тоже. По общему признанию эти два очень редко используются.
44

Жадный означает, что ваше выражение будет соответствовать как можно большему количеству групп, ленивое означает, что оно будет соответствовать наименьшей возможной группе. Для этой строки:

abcdefghijklmc

и это выражение:

a.*c

Жадный матч будет соответствовать всей строке, а ленивый матч будет соответствовать только первому abc.

10

Насколько я знаю, большинство движков regex жадно по умолчанию. Добавить знак вопроса в конце квантификатора позволит ленивое совпадение.

Как упоминалось в комментарии @Andre S.

  • Жадный: продолжайте поиск до тех пор, пока условие не будет выполнено.
  • Lazy: прекратить поиск, когда условие выполнено.

Обратитесь к приведенному ниже примеру для жадного и ленивого.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test {
    public static void main(String args[]){
        String money = "100000000999";
        String greedyRegex = "100(0*)";
        Pattern pattern = Pattern.compile(greedyRegex);
        Matcher matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm greeedy and I want " + matcher.group() + " dollars. This is the most I can get.");
        }

        String lazyRegex = "100(0*?)";
        pattern = Pattern.compile(lazyRegex);
        matcher = pattern.matcher(money);
        while(matcher.find()){
            System.out.println("I'm too lazy to get so much money, only " + matcher.group() + " dollars is enough for me");
        }
    }
}


Результат:

Я greeedy и хочу 100000000 долларов. Это самое лучшее, что я могу получить.

Мне слишком ленив, чтобы заработать столько денег, мне достаточно всего 100 долларов

  • 3
    Мне очень нравится твой пример.
8

Взято из www.regular-expressions.info

Жадность: жадные кванторы сначала пытаются повторить токен столько раз насколько это возможно, и постепенно отказывается от совпадений, когда двигатель отступает, чтобы найти общий матч.

Лень: ленивый квантификатор сначала повторяет токен столько раз, сколько требуется, и постепенно расширяет совпадение, так как двигатель возвращается к регулярному выражению найти общий результат.

5

От Регулярное выражение

Стандартные кванторы в регулярных выражения жадные, то есть они как можно больше, только давая при необходимости, чтобы соответствовать остаток регулярного выражения.

Используя ленивый квантификатор, выражение пытается минимальное совпадение первый.

2

Лучше всего показано на примере. Строка. 192.168.1.1 и жадное регулярное выражение \b. +\B Вы могли бы подумать, что это даст вам 1-й октет, но на самом деле совпадает со всей строкой. ЗАЧЕМ!!! Поскольку. + Жадный и жадный матч соответствует каждому символу в '192.168.1.1', пока он не достигнет конца строки. Это важный бит!!! Теперь он начинает возвращать один символ за раз, пока не найдет совпадение для третьего токена (\ b).

Если строка с текстовым файлом 4 ГБ и 192.168.1.1 была в начале, вы могли бы легко увидеть, как эта обратная трассировка вызовет проблему.

Чтобы сделать регулярное выражение, не жадное (ленивое), поставить знак вопроса после вашего жадного поиска, например *? ?? +? Теперь происходит токен 2 (+?) Находит совпадение, регулярное выражение перемещается вдоль символа, а затем пытается использовать следующий токен (\ b), а не токен 2 (+?). Таким образом, он ползет осторожно.

1

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

Lazy остановится, как только он встретит первый шаблон, который вы запросили.

Одним из распространенных примеров, с которыми я часто сталкиваюсь, является \s*-\s*? регулярного выражения ([0-9]{2}\s*-\s*?[0-9]{7})

Первый \s* классифицируется как жадный из-за * и будет выглядеть как можно больше белых пробелов после того, как будут встречаться цифры, а затем найдите тире "-". Где второй \s*? является ленивым из-за присутствия *?, что означает, что он будет выглядеть первым символом пробела и останавливаться прямо там.

1

Жадное соответствие. Поведение регулярных выражений по умолчанию должно быть жадным. Это означает, что он пытается извлечь как можно больше, пока не будет соответствовать шаблону, даже если меньшая часть была бы синтаксически достаточной.

Пример:

import re
text = "<body>Regex Greedy Matching Example </body>"
re.findall('<.*>', text)
#> ['<body>Regex Greedy Matching Example </body>']

Вместо сопоставления до первого появления ' > , он извлекал всю строку. Это жадность по умолчанию или "взять все поведение регулярного выражения".

Lazy matching, с другой стороны, "берет как можно меньше. Это можно сделать, добавив ? в конец шаблона.

Пример:

re.findall('<.*?>', text)
#> ['<body>', '</body>']

Если вы хотите получить только первое совпадение, используйте вместо этого метод поиска.

re.search('<.*?>', text).group()
#> '<body>'

Источник: Примеры Python Regex

-2

попытайтесь понять следующее поведение:

    var input = "0014.2";

Regex r1 = new Regex("\\d+.{0,1}\\d+");
Regex r2 = new Regex("\\d*.{0,1}\\d*");

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // "0014.2"

input = " 0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // " 0014"

input = "  0014.2";

Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // ""

Ещё вопросы

Сообщество Overcoder
Наверх
Меню