Жадный и хочет, и Притяжательные квантификаторы


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

в частности, в следующем примере:

Enter your regex: .*foo  // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.

в объяснении упоминается ел вся входная строка, буквы были потреблено, matcher отступление, самое правое вхождение "foo" имеет был выплюнул и т. д.

к сожалению, несмотря на приятные метафоры, я до сих пор не понимаю, что кто ест... Вы знаете еще один учебник, который объясняет (кратко) как движки регулярных выражений работают?

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

в первом примере используется жадный квантор. * находить "ничего", ноль или больше раз, а затем буквы "Ф" "О" "о". Потому что Квантор-это жадный, то .* часть выражение сначала съедает весь вход строка. На данный момент, общий выражение не может быть успешным, потому что последние три буквы ("f "" o "" o") имеют уже был потреблен (кем?). Итак, сопоставитель медленно отступает (справа-налево?) по одной букве за раз до самого правого вхождения "фу" был срыгнул (что это значит?), в котором точки совпадения и поиск заканчивается.

второй пример, однако, неохотно, поэтому он начинается с первого потребляя (кем?) "ничего". Потому что "фу" не появляется в начале строка, она вынуждена глотать (кто ласточки?) этот первая буква ("x"), которая вызывает первый матч при 0 и 4. Наш тест жгут продолжает процесс до этот входная строка исчерпана. Оно находит еще один матч на 4 и 13.

третий пример не удается найти a совпадение, потому что Квантор притяжательный. В этом случае весь входная строка потребляется .* + , (как?) не оставляя ничего, чтобы удовлетворить "фу" в конце выражение. Используйте притяжательный Квантор для ситуаций, когда вы хотите захватить все что-то без когда-либо отступать (что означает back off?); it будет эффективнее эквивалентный жадный Квантор в случаи, когда совпадения нет сразу найти.

7 296

7 ответов:

я дам ему выстрелили.

A жадина Квантор сначала соответствует как можно больше. Так что .* соответствует всей строке целиком. Затем сопоставитель пытается соответствовать f следующие, но там не осталось символов. Таким образом, он "отступает", заставляя жадный Квантор соответствовать одной вещи меньше (оставляя "o" в конце строки непревзойденным). Это все еще не соответствует f в регулярном выражении, поэтому он "отступает" еще на один шаг, делая жадный Квантор сопоставьте еще одну вещь (оставив" oo " в конце строки непревзойденным). Это еще не соответствует f в регулярном выражении, поэтому он отступает еще на один шаг (оставляя "foo" в конце строки непревзойденным). Теперь, сопоставитель, наконец, соответствует f в соответствии с o и далее o совпадают тоже. Успех!

A хочет или" не жадный " Квантор сначала соответствует как можно меньше. Так что .* сначала ничего не соответствует, оставляя всю строку непревзойденной. Затем сопоставитель пытается соответствовать f следующий, но непревзойденная часть строки начинается с "x", так что это не работает. Таким образом, сопоставитель отступает, делая не жадный Квантор совпадающим еще с одной вещью (теперь он соответствует "x", оставляя "fooxxxxxxfoo" непревзойденным). Затем он пытается соответствовать f, который преуспевает, и o и далее o в матче регулярного выражения. Успех!

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

A собственницей Квантор так же, как жадный Квантор, но он не отступает. Так что все начинается с .* соответствие всей строки, не оставляя ничего непревзойденного. Тогда ему ничего не остается, чтобы соответствовать f в регулярное выражение. Поскольку притяжательный Квантор не возвращается, совпадение там терпит неудачу.

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

Visual Image

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

важно понимать, что большинство двигателей регулярных выражений-это откат: они будут ориентировочно примите потенциальное, частичное совпадение, пытаясь сопоставьте все содержимое регулярного выражения. Если регулярное выражение не может быть полностью совпали с первой попытки, то регулярное выражение будет вернуться назад на одном из его матчей. Он будет пытаться соответствовать *,+,?, чередования, или {n,m} повторите по-другому,и повторите попытку. (И да, этот процесс можете займет много времени.)

в первом примере используется жадный квантор. * чтобы найти "что-нибудь", ноль или больше раз, а затем письмо "Ф" "О" "о". Потому что Квантор-это жадный, то .* часть выражение сначала съедает весь вход строка. На данный момент, общий выражение не может быть успешным, потому что последние три буквы ("f "" o "" o") имеют уже был потреблен (кем?).

последние три буквы f,o и o уже были поглощены начальным .* часть правила. Однако следующий элемент в регулярном выражении, f, ничего не осталось во входной строке. Двигатель будет вынужден вернуться назад на его начальном .* матч, и попробуйте сопоставить все-но-последний символ. (Это может быть умный и вернуться ко всем, кроме последних трех, потому что он имеет три буквальных термина, но я не знаю деталей реализации на этом уровне.)

Итак, сопоставитель медленно отступает (справа-налево?) по одной букве за раз пока самое правое возникновение "фу" был отрыгнут (что это значит?), где

это означает foo С ориентировочно в том числе при сопоставлении .*. Поскольку эта попытка не удалась, механизм регулярных выражений пытается принять на один символ меньше в .*. Если бы был удачный матч до the .* в этом примере, то двигатель, вероятно, попытается сократить .* матч (с справа налево, как вы указали, потому что это жадный квалификатор), и если он не смог сопоставить все входные данные, то он может быть вынужден переоценить то, что он соответствовал до the .* в моем гипотетическом примере.

точка совпадения и поиск заканчивается.

второй пример, однако, неохотно, поэтому он начинается с первого потребляя (кем?) "ничего". Потому что "фу"

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

не появляется в начале строка, она вынуждена глотать (кто ласточки?) в

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

первая буква ("x"), которая вызывает первый матч при 0 и 4. Наш тест жгут продолжает процесс до входная строка исчерпана. Оно находит еще один матч на 4 и 13.

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

A .*+ будет потреблять как можно больше, и не будет отступать чтобы найти новые совпадения, когда регулярное выражение в целом не может найти совпадение. Поскольку притяжательная форма не выполняет обратное отслеживание, вы, вероятно, не увидите много применений с .*+, но скорее с классами символов или аналогичными ограничениями:account: [[:digit:]]*+ phone: [[:digit:]]*+.

это может значительно ускорить сопоставление регулярных выражений, потому что вы сообщая механизму регулярных выражений, что он никогда не должен возвращаться к потенциальным совпадениям, если вход не совпадает. (Если бы вам пришлось написать весь соответствующий код вручную, это было бы похоже на никогда не использовать putc(3) чтобы "отодвинуть" входной символ. Это было бы очень похоже на наивный код, который можно было бы написать с первой попытки. За исключением того, что двигатели регулярных выражений намного лучше, чем один символ push-back, они могут перемотать все назад к нулю и повторить попытку. :)

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

не оставляя ничего, чтобы удовлетворить "фу" в конце выражение. Используйте притяжательный Квантор для ситуаций, когда вы хотите захватить все что-то без когда-нибудь отступать (что означает back off?); он будет опережать

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

эквивалентный жадный Квантор в случаи, когда совпадения нет сразу найти.

http://swtch.com / ~rsc/regexp/regexp1.html

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

Если вам нужен более высокий уровень (менее подробное объяснение), для простых регулярных выражений, таких как тот, который вы смотрите, механизм регулярных выражений работает путем обратного отслеживания. По сути, он выбирает ("ест") раздел строки и пытается сопоставить регулярное выражение с этим разделом. Если он соответствует, отлично. Если нет, движок изменяет свой выбор раздела строки и пытается сопоставить регулярное выражение с этим разделом и т. д., пока он не попробует все возможные варианты.

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

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

  • жадный Квантор говорит двигателю начать с весь строка (или, по крайней мере, все это, что еще не было сопоставлено с предыдущими частями регулярное выражение) и проверить, соответствует ли оно регулярному выражению. Если это так, отлично; двигатель может продолжить работу с остальной частью регулярного выражения. Если нет, он пытается снова, но обрезает один символ (последний) из раздела строки, которая будет проверена. Если это не работает, он обрезает другой символ и т. д. Таким образом, жадный Квантор проверяет возможные совпадения в порядке от самого длинного до самого короткого.

  • неохотный Квантор говорит двигателю начать с самого короткого возможного куска строка. Если он соответствует, двигатель может продолжить; если нет, то это добавляет один символ в раздел проверяемой строки и пытается это сделать, и так далее, пока он не найдет совпадение или вся строка не будет использована. Таким образом, неохотный Квантор проверяет возможные совпадения в порядке от самого короткого до самого длинного.

  • притяжательный Квантор похож на жадный Квантор с первой попытки: он говорит движку начать, проверяя всю строку. Разница если это не сработает, притяжательный Квантор сообщает, что матч не удался прямо тогда и там. Движок не изменяет секцию рассматриваемой строки, и он больше не делает попыток.

вот почему притяжательный Квантор совпадения терпит неудачу в вашем примере:.*+ проверяется по всей строке, которая ему соответствует, но затем движок продолжает искать дополнительные символы foo после этого - но, конечно, это не найдите их, потому что вы уже в конце строки. Если бы это был жадный Квантор, он бы отступил и попытался сделать .* только совпадение до предпоследнего символа, затем до третьего до последнего символа, затем до четвертого до последнего символа, который преуспевает, потому что только тогда есть foo оставшееся после .* "съел" более раннюю часть строки.

вот мой взять с помощью ячейки и индекса позиции (см. здесь схемы чтобы отличить ячейку от индекса).

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

Входной Строки: xfooxxxxxxfoo
регулярное выражение: .* foo

выше выражение состоит из двух частей:
(я.')*' и
(ii)' foo'

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

Шаг 1:
(я. )* = xfooxxxxxxfoo-PASS ('.* 'является жадным квантором и будет использовать всю входную строку)
(ii)foo = после индекса 13 не осталось ни одного символа-FAIL
Матч не удался.

Шаг 2:
(я. )* = xfooxxxxxxfo-PASS (возврат на жадный Квантор '.*')
(ii) foo = o - FAIL
Матч не удался.

Шаг 3:
(я. )* = xfooxxxxxxf-PASS (возврат на жадный Квантор '.*')
(ii) foo = oo - FAIL
Матч не удался.

Шаг 4:
(я. )* = xfooxxxxxx-PASS (возврат на жадный Квантор '.*')
(ii) foo = foo - PASS
Отчет матч

результат: 1 матч(Эс)
Я нашел текст "xfooxxxxxxfoo", начинающийся с индекса 0 и окончание в индексе 13.

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

Входной Строки: xfooxxxxxxfoo
регулярное выражение: .* ?фу

выше регулярное выражение состоит из двух частей:
(я.' )* ? и
(ii)'foo'

Шаг 1:
.* ? = "(пустой) - проход (совпадение как немного неохотно Квантор '.* ?'. Индекс 0 наличие " - это совпадение.)
foo = XFO-FAIL (ячейка 0,1,2-т. е. индекс между 0 и 3)
Матч не удался.

Шаг 2:
.* ? = X-PASS (добавление символов в неохотный Квантор '.* ?'. Ячейка 0, имеющая 'x', является совпадением.)
foo = foo-PASS
Отчет матч

Шаг 3:
.* ? = "(пустой) - PASS (матч как можно меньше, чтобы неохотно Квантор '.* ?'. Индекс 4 Наличие " - это совпадение.)
foo = xxx-FAIL (ячейка 4,5,6-т. е. индекс между 4 и 7)
Матч не удался.

Шаг 4:
.* ? = X-PASS (добавление символов в неохотный Квантор '.* ?'. Ячейка 4.)
foo = xxx-FAIL (ячейка 5,6,7-т. е. индекс между 5 и 8)
Матч не удался.

Шаг 5:
.* ? = xx-PASS (добавить символы в неохотно квантор.' * ?'. Камера с 4 по 5.)
foo = xxx-FAIL (ячейка 6,7,8-т. е. индекс между 6 и 9)
Матч не удался.

Шаг 6:
.* ? = xxx-PASS (добавление символов в неохотный Квантор '.* ?'. Камера с 4 по 6.)
foo = xxx-FAIL (ячейка 7,8,9-т. е. индекс между 7 и 10)
Матч не удался.

Шаг 7:
.* ? = xxxx-PASS (добавление символов в неохотный Квантор '.* ?'. Ячейка 4 через 7.)
foo = xxf-FAIL (ячейка 8,9,10-т. е. индекс между 8 и 11)
Матч не удался.

Шаг 8:
.* ? = xxxxx-PASS (добавление символов в неохотный Квантор '.* ?'. Камера с 4 по 8.)
foo = XFO-FAIL (ячейка 9,10,11-т. е. индекс между 9 и 12)
Матч не удался.

Шаг 9:
.* ? = xxxxxx-PASS (добавление символов в неохотный Квантор '.* ?'. Камера с 4 по 9.)
foo = Foo-PASS (ячейка 10,11,12-т. е. индекс между 10 и 13)
Отчет матч

Шаг 10:
.* ? = "(blank) - PASS (сопоставьте как можно меньше с неохотным квантором '.* ?'. Индекс 13-это пустое.)
foo = не осталось символов, чтобы соответствовать-FAIL (нет ничего после индекса 13, Чтобы соответствовать)
Матч не удался.

результат: 2 матча
Я нашел текст "xfoo" начиная с индекса 0 и заканчивая индексом 4.
Я нашел текст "xxxxxxfoo", начиная с индекса 4 и заканчивая индексом 13.

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

Входной Строки: xfooxxxxxxfoo
регулярное выражение: .* +foo

выше регулярное выражение состоит из двух частей: '.*+' и 'фу'.

Шаг 1:
.* + = xfooxxxxxxxfoo-PASS (максимально соответствует притяжательному квантор.' *')
foo = не осталось символов, чтобы соответствовать-FAIL (ничего не соответствует после индекса 13)
Матч не удался.

Примечание: отступление не допускается.

результат: 0 match (es)

жадный: "сопоставьте самую длинную возможную последовательность символов"

неохотно: "соответствует кратчайшей последовательности символов"

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

кстати: никакая реализация сопоставления шаблонов регулярных выражений никогда не будет использовать обратное отслеживание. Все реальные pattern matcher чрезвычайно быстры - почти не зависят от сложности обычного выражение лица!

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

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

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

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

Притяжательные Квантификации нет карантин и включает все в фиксированном активный последовательность.