В чем разница между.*? и. * регулярные выражения?

Я пытаюсь разбить строку на две части с помощью regex. Строка форматируется следующим образом:

text to extract<number>

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

Ответ 1

Это разница между жадными и не жадными кванторами.

Рассмотрим вход 101000000000100.

Использование 1.*1, * является жадным - оно будет соответствовать всему пути до конца, а затем возвращается назад, пока оно не будет соответствовать 1, оставив вас с 1010000000001.
.*? не является жадным. * ничего не будет соответствовать, но затем будет пытаться сопоставить дополнительные символы, пока не будет соответствовать 1, в итоге получим 101.

Все кванторы имеют нежелательный режим: .*?, .+?, .{2,6}? и даже .??.

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

См. Шифр ​​Quantifier.

Ответ 2

О жадных против неживых

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

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

Ссылки


Пример 1: от A до Z

Сравним эти два шаблона: A.*Z и A.*?Z.

Учитывая следующий ввод:

eeeAiiZuuuuAoooZeeee

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

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

eeeAiiZuuuuAoooZeeee
   \_______________/
    A.* matched, Z can't match

Так как Z не соответствует, обратный путь движка и .* должны совпадать с меньшим числом .:

eeeAiiZuuuuAoooZeeee
   \______________/
    A.* matched, Z still can't match

Это происходит еще несколько раз, пока, наконец, мы не придем к следующему:

eeeAiiZuuuuAoooZeeee
   \__________/
    A.* matched, Z can now match

Теперь Z может совпадать, поэтому общий шаблон соответствует:

eeeAiiZuuuuAoooZeeee
   \___________/
    A.*Z matched

Напротив, повторное повторение в A.*?Z сначала соответствует как можно меньше ., а затем при необходимости принимает больше .. Это объясняет, почему он находит два совпадения на входе.

Здесь представлено визуальное представление того, что соответствовали двум шаблонам:

eeeAiiZuuuuAoooZeeee
   \__/r   \___/r      r = reluctant
    \____g____/        g = greedy

Пример: альтернативный

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

Образец A[^Z]*Z также находит те же два совпадения, что и шаблон A.*?Z для указанного выше ввода (как видно на ideone.com). [^Z] - это то, что называется отрицательным символьным классом: оно соответствует любому, кроме Z.

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

Ссылки


Пример 2: от A до ZZ

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

eeAiiZooAuuZZeeeZZfff

Это совпадения для указанного выше ввода:

Здесь представлено визуальное представление того, что они соответствовали:

         ___n
        /   \              n = negated character class
eeAiiZooAuuZZeeeZZfff      r = reluctant
  \_________/r   /         g = greedy
   \____________/g

Связанные темы

Это ссылки на вопросы и ответы на stackoverflow, которые охватывают некоторые темы, которые могут представлять интерес.

В одном жадном повторении можно отказаться от другого

Ответ 3

Скажем, у вас есть:

<a></a>

<(.*)> будет соответствовать a></a, где <(.*?)> будет соответствовать a. Последний останавливается после первого совпадения >. Он проверяет один или 0 совпадений .*, за которым следует следующее выражение.

Первое выражение <(.*)> не останавливается при сопоставлении первого >. Он будет продолжаться до последнего совпадения >.