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

Что это за два термина в понятной форме?


person ajsie    schedule 20.02.2010    source источник
comment
См. Также stackoverflow.com/questions/3075130 /   -  person polygenelubricants    schedule 24.08.2010


Ответы (12)


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

<em>Hello World</em>

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

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

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

person Sampson    schedule 20.02.2010
comment
Итак, если вы используете жадный, у вас будет 3 совпадения (1 элемент + 2 тега) или только 1 совпадение (1 элемент)? - person ajsie; 20.02.2010
comment
Он будет соответствовать только 1 раз, начиная с первого и заканчивая последним . - person Sampson; 20.02.2010
comment
Но если сделать его ленивым, это приведет к двойному совпадению, давая нам и открывающий, и закрывающий теги, игнорируя текст между ними (поскольку он не соответствует выражению). - person Sampson; 20.02.2010
comment
Еще один отличный инструмент, который я всегда использую: debuggex.com. Он также имеет функцию «Вставить в StackOverflow». - person Ron van der Heijden; 27.05.2014
comment
Просто добавлю, что есть жадный способ сделать это: <[^>]+> regex101.com/r/lW0cY6 / 1 - person alanbuchanan; 15.06.2015
comment
Если кто-то хочет глубже понять, как ленивые и жадные квантификаторы работают с необязательными промежуточными подшаблонами, отметьте регулярное выражение Perl, соответствующее необязательной фразе в длинном предложении. - person Wiktor Stribiżew; 22.04.2016
comment
Для записи об использовании регулярного выражения с HTML stackoverflow.com/questions/1732348/ - person qwr; 12.06.2021

«Жадный» означает совпадение самой длинной строки из возможных.

Ленивый означает соответствие самой короткой строке.

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

person slebetman    schedule 20.02.2010
comment
Блестяще, так что ленивый остановится, как только условие l будет выполнено, но жадный означает, что он остановится только тогда, когда условие l больше не будет выполнено? - person Andrew S; 24.02.2014
comment
Для всех, кто читает этот пост: жадные или ленивые квантификаторы сами по себе не будут соответствовать самой длинной / самой короткой подстроке. Вам нужно будет использовать либо умеренный жадный токен или используйте подходы, отличные от регулярных выражений. - person Wiktor Stribiżew; 16.10.2016
comment
@AndrewS Пусть вас не смущает двойной ll в примере. Довольно ленивый будет соответствовать самой короткой подстроке, в то время как жадный найдет самую длинную из возможных. Жадный h.+l соответствует 'helol' в 'helolo', а ленивый h.+?l соответствует 'hel'. - person v.shashenko; 21.03.2017
comment
Разве ? не делает .+ необязательным в h.+?l. Разве не для этого нужен ?? Кроме того, как бы вы различили две функции ? - person FloatingRock; 14.04.2017
comment
@FloatingRock: Нет. x? означает, что x не является обязательным, но +? - это другой синтаксис. Это означает, что перестаньте искать то, что вам подходит - ленивое сопоставление. - person slebetman; 14.04.2017
comment
@FloatingRock: Что касается того, как вы различаете разные синтаксисы, просто: ? означает необязательный, а +? означает ленивый. Следовательно, \+? означает, что + не является обязательным. - person slebetman; 14.04.2017

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: _ 13_ вывод: stackoverflo w
выражение lazy reg: _ 14_ вывод: stacko verflow

person Premraj    schedule 15.01.2016
comment
не является ?? эквивалентно? . Точно так же не {n}? эквивалентно {n} - person Number945; 02.09.2016
comment
@BreakingBenjamin: нет ?? не эквивалентно?, когда у него есть выбор, вернуть 0 или 1 вхождение, он выберет 0 (ленивую) альтернативу. Чтобы увидеть разницу, сравните re.match('(f)?(.*)', 'food').groups() с re.match('(f)??(.*)', 'food').groups(). В последнем случае (f)?? не будет соответствовать ведущей букве «f», хотя и может. Следовательно, 'f' будет соответствовать второй группе захвата '. *'. Я уверен, что вы можете построить пример с помощью '{n}?' тоже. По общему признанию, эти два используются очень редко. - person smci; 16.11.2017
comment
@ Number945 Да, {n}? эквивалентно {n}. См. stackoverflow.com/questions/18006093/how -до-и-и-отличается - person Théophile; 01.04.2021

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

abcdefghijklmc

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

a.*c

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

person Carl Norum    schedule 20.02.2010

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

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

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

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

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


Результат:

I'm greeedy and I want 100000000 dollars. This is the most I can get.

I'm too lazy to get so much money, only 100 dollars is enough for me
person Eugene    schedule 09.11.2016

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

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

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

person Suganthan Madhavan Pillai    schedule 19.10.2014
comment
Это кажется наиболее правильным определением лени по сравнению с ответами, получившими больше голосов. В других ответах, похоже, опускается концепция, согласно которой при лени двигатель постепенно расширяет совпадение ... чтобы найти общее совпадение. - person alx9r; 30.12.2020

Из Регулярное выражение

Стандартные квантификаторы в регулярных выражениях являются жадными, то есть они соответствуют столько, сколько могут, возвращая только столько, сколько необходимо для соответствия остатку регулярного выражения.

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

person Adriaan Stander    schedule 20.02.2010

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

Пример:

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

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

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

Пример:

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

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

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

Источник: Примеры регулярных выражений Python

person Selva    schedule 21.01.2018

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

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

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

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

person stackFan    schedule 06.02.2018
comment
Я не понимаю, чем это регулярное выражение отличается от использования ленивого квантификатора. \s может соответствовать только пробелу, и после него должно быть 7 цифр. - person Scratte; 03.02.2021

Лучше всего показать на примере. Нить. 192.168.1.1 и жадное регулярное выражение \b.+\b Вы можете подумать, что это даст вам 1-й октет, но на самом деле он совпадает со всей строкой. Почему? Поскольку. + Является жадным, и жадное совпадение соответствует каждому символу в 192.168.1.1, пока не достигнет конца строки. Это важный момент! Теперь он начинает возвращаться по одному символу за раз, пока не найдет совпадение для 3-го токена (\b).

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

Чтобы сделать регулярное выражение не жадным (ленивым), поставьте вопросительный знак после жадного поиска, например

*?
??
+?

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

person Jason Alcock    schedule 12.03.2018

Жадные квантификаторы похожи на IRS / ATO

Если оно есть, они все заберут.

IRS соответствует этому регулярному выражению: .*

$50,000

Это будет соответствовать всему!

См. Здесь пример: Жадный пример

Нежадные квантификаторы - они берут как можно меньше

Если я прошу возврат налога, IRS внезапно становится не жадным, и они используют этот количественный показатель:

(.{2,5}?)([0-9]*) против этого ввода: $50,000

Первая группа не является нуждающейся и соответствует только $5, поэтому я получаю $5 возмещение в размере 50 000 долларов. Они не жадные. Берут как можно меньше.

См. Здесь: Нежадный пример.

Зачем беспокоиться?

Это становится важным, если вы пытаетесь сопоставить определенные части выражения. Иногда не хочется сочетать все.

Надеюсь, эта аналогия поможет вам вспомнить!

person BKSpurgeon    schedule 28.01.2020

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

    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); // ""
person FrankyHollywood    schedule 30.10.2016
comment
Что это за язык? - person Scratte; 03.02.2021
comment
Он использовал язык C #. - person AndreFeijo; 26.02.2021