Понимание квантификаторов
Я проходил через Java Tutorial on Quantifiers.
Существует различие, упомянутое среди различий среди жадных, неохотных и властных квантификаторов.
Я не могу понять, в чем разница.
Объяснение представлено следующим образом:
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.
В первом примере используется жадный квантификатор. * для поиска "ничего", ноль или более раз, за которым следуют буквы "f" "o" "o". Поскольку квантификатор является жадным, часть выражения. * Сначала вырабатывает всю входную строку. На этом этапе общее выражение не может быть успешным, потому что последние три буквы ( "f" "o" "o" ) уже были использованы. Таким образом, помощник медленно откладывает одну букву за раз до тех пор, пока правильное появление "foo" не будет опрокинуто, после чего совпадение завершится успешно и поиск завершится.
Второй пример, однако, неохотно, поэтому он начинается с первого потребления "ничего". Поскольку "foo" не появляется в начале строки, он вынужден усвоить первую букву ( "x" ), которая запускает первое совпадение с 0 и 4. Наш тестовый жгут продолжит процесс до тех пор, пока строка ввода не будет истощены. Он находит другое совпадение в 4 и 13.
Третий пример не может найти совпадение, потому что квантификатор является притяжательным. В этом случае вся входная строка потребляется. * +, Не оставляя ничего лишнего, чтобы удовлетворить "foo" в конце выражения. Используйте притяжательный квантификатор для ситуаций, когда вы хотите захватить все что-либо, не отступая; он будет превосходить эквивалентный жадный квантификатор в случаях, когда совпадение не будет найдено сразу.
Ответы
Ответ 1
Основное различие между ленивым (неохотным) и жадным падежами состоит в том, что поведение структуры возврата и притяжательного падежа слишком агрессивно !
- Ленивый регистр всегда, после одного совпадения, уступит фокусировку соответствующего движка следующему оператору в регулярном выражении. Если следующий оператор завершится неудачно, структура возврата будет вынуждена повторять ленивый случай, и это будет продолжаться до тех пор, пока не завершатся операторы или целевой текст; например, в вашем примере переход к совпадению char
f
после каждого успешного совпадения, поэтому всякий раз, когда у вас есть фраза foo
, вы получаете совпадение, поэтому мы получаем несколько совпадений от ее использования.
. *? Foo
x fooxxxxxxfoo
В начале ленивый регистр будет иметь успешное совпадение с x
(после успешного пустого совпадения) и передаст фокус следующему оператору; foo
часть регулярного выражения, и так как он присутствует после x
, мы получаем совпадение для этого фрагмента, то же самое для второй части строки.
- Жадный случай противоположен, он продолжит свое сопоставление до тех пор, пока не потерпит неудачу, никогда не передаст фокус следующему оператору, только когда неудачное сопоставление вступит в силу, обратное отслеживание вступит в силу, и следующие операторы будут сопоставлены с обратного;
. * Foo
xfooxxxxxxfo o
Когда в этой точке находится жадный регистр (последний символ), сопоставление будет неудачным, поскольку мы не можем сопоставить часть foo
регулярного выражения. Затем обратное отслеживание заставит жадный случай backtrace
свои шаги и применить следующий оператор foo
, аналогичный ленивому падежу;
xfooxxxxxx f oo
На этом этапе часть foo
будет иметь успешное совпадение и, таким образом, завершится успешным совпадением всей строки.
- Притяжательный падеж очень похож на жадный падеж, за исключением последней части, где провал матча вызывает
backtracking
, это не относится к притяжательному падежу. Если он может быть сопоставлен, он будет обладать и принесет в жертву успех матча в процессе. Если бы не удалось сопоставить символ, только тогда фокус перешел бы к следующему оператору регулярного выражения.
. * +foo
xfooxxxxxxfo o
Подобный жадному падежу, мы достигли конца строки, но притяжательный падеж все еще может соответствовать ему, таким образом, не будет передавать факел в структуру backtrack
и вызовет сбой сопоставления.
Ответ 2
Общие правила
Подразумевается базовое знание кванторов ?
, *
и +
(соответственно, "нуль или один", "ноль или более", "один или несколько" ).
- Мы говорим, что квантификатор жадный, если он пытается выработать как можно больше символов.
- Мы говорим, что квантификатор неохотно (ленивый), если он пытается выработать меньше символов, насколько это возможно.
- Мы говорим, что квантификатор притяжательный, если он жадный, и он не позволяет выполнить обратную передачу.
Вы можете понять, что означает "backtrace", только если вы знаете, как работают парсеры regex (см. ниже "Динамический пример" ).
Объяснения по одному случаю
-
?
: первое появление теста 1, затем 0; если вы нашли 1 вхождение, а затем вам нужно отбросить его, вы можете сделать это.
-
??
: первый тест 0 вхождений, затем 1
-
?+
: первое появление теста 1, затем 0; если вы нашли 1 вхождение, а затем вам нужно отбросить его, вы не можете сделать это
-
*
: попытайтесь получить как можно больше случаев (даже 0); если вы обнаружили N вхождений, а затем вам нужно отбросить (некоторые из них), вы можете сделать это, начиная с последнего
-
*?
: попытайтесь получить меньше вхождений (даже 0)
-
*+
: попытайтесь получить как можно больше случаев (даже 0); если вы обнаружили N вхождений, а затем вам нужно отбросить (некоторые из них), вы не можете сделать это
-
+
: попытайтесь получить как можно больше случаев (не менее 1); если вы обнаружили N вхождений, а затем вам нужно отбросить (некоторые из них), вы можете сделать это, начиная с последнего
-
+?
: попытайтесь получить как можно меньше эпизодов (не менее 1)
-
++
: попытайтесь получить как можно больше случаев (не менее 1); если вы обнаружили N вхождений, а затем вам нужно отбросить (некоторые из них), вы не можете сделать это
Динамический пример
В этом разделе я попытаюсь показать вам, что является логикой парсера регулярных выражений:
1) Случай /.*foo/
:
Сначала это поворот подшаблона /.*/
. Он начинает разработку первого символа:
xfooxxxxxxfoo
^
Затем он пытается выработать как можно больше символов:
xfooxxxxxxfoo
^^
xfooxxxxxxfoo
^^^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^^
Курсор достиг конца, но подшаблон /foo/
еще не сыграл свою роль. Поэтому курсор "возвращается", чтобы позволить подшаблону /foo/
получить соответствие:
xfooxxxxxxfoo
^^^^^^^^^^^^
/foo/
все еще не может получить соответствие, поэтому нам нужно вернуться снова:
xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^
Теперь подшаблон /foo/
может получить соответствие:
xfooxxxxxxfoo
^^^^^^^^^^^^^
Таким образом, совпадение представляет собой целую строку xfooxxxxxxfoo
.
2) Случай /.*?foo/
:
Сначала это поворот подшаблона /.*?/
. Это лениво, поэтому нам бы хотелось, чтобы он упустил 0 персонажей. Но если это так, то подшаблон /foo/
не смог бы получить соответствие, поэтому он должен разработать один символ:
xfooxxxxxxfoo
^
Теперь он foo
поворачивается:
xfooxxxxxxfoo
^^^^
Итак, совпадение xfoo
.
(если вы установите тип global
для регулярного выражения, тогда синтаксический анализатор перезапустится с первого символа после совпадения, давая второе совпадение xxxxxxfoo
)
3) Случай /.*+foo/
:
Сначала это поворот подшаблона /.*+/
. Он пытается выработать как можно больше символов:
xfooxxxxxxfoo
^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^^^
Курсор достиг конца, но подшаблон /foo/
еще не сыграл свою роль. Итак, курсор "возвращается"... о нет, как жаль, он не может (потому что он притяжателен)!
Итак, у нас нет совпадений.