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


142

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

text to extract<number>

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


Ответы:


172

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

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

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

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

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

См. Шпаргалку квантификатора .


Ах, здорово, мне нравится этот последний из всего, кроме знака>!
Дуг

1
Можете ли вы объяснить или показать пример того, как жадный ?отличается от не жадного ???
AdrianHHH

4
Конечно. Для строки "abc", регулярное выражение /\w\w?\w/будет соответствовать полной строке "abc"- потому что ?это жадный. /\w\w??\w/ленивый - он будет только соответствовать "ab". Он будет возвращаться и совпадать только в "abc"случае неудачи позже.
Коби

184

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

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

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

Ссылки


Пример 1: от А до Я

Давайте сравним эти две модели: 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, которые охватывают некоторые темы, которые могут представлять интерес.

Одно жадное повторение может перерасти другое


1
Я хотел сказать rubular.com, а не ideone.com. Для других: не редактируйте этот пост для меня, я сделаю это сам в следующей редакции вместе с другими примерами. Не стесняйтесь оставлять отзывы, предложения и т. Д. В комментариях, чтобы я мог также включить их.
полигенасмазочные материалы


4
Этот ответ был добавлен в FAQ по регулярному выражению переполнения стека , в разделе «Квантификаторы> Подробнее о различиях ...»
aliteralmind

Этот ответ действительно заслуживает того, чтобы быть выбранным ответом! Большое спасибо за ваше подробное объяснение.
masky007

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

21

Допустим, у вас есть:

<a></a>

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

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


это легче понять, чем приведенное выше объяснение.
Прометей
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.