Ответ 2
Первый более явный, i. е. он определенно исключает закрывающий разделитель как часть согласованного текста. Это не гарантируется во втором случае (если регулярное выражение расширено, чтобы соответствовать больше, чем только этот тег).
Пример. Если вы попытаетесь сопоставить <tag1><tag2>Hello!
с <.*?>Hello!
, регулярное выражение будет соответствовать
<tag1><tag2>Hello!
тогда как <[^>]*>Hello!
будет соответствовать
<tag2>Hello!
Ответ 3
То, что большинство людей не учитывает при приближении к таким вопросам, как это происходит, когда регулярное выражение не может найти совпадение. Именно тогда наиболее вероятны появляться всплески производительности убийцы. Например, возьмите пример Тима, где вы ищете что-то вроде <tag>Hello!
. Рассмотрим, что происходит с:
<.*?>Hello!
Механизм regex находит <
и быстро находит закрытие >
, но не >Hello!
. Таким образом, .*?
продолжает поиск >
, за которым следует Hello!
. Если его нет, он дойдет до конца документа до его сдачи. Затем двигатель regex возобновляет сканирование, пока не найдет другой <
, и повторит попытку. Мы уже знаем, как это получится, но механизм регулярных выражений, как правило, не работает; он проходит через тот же rigamarole с каждым <
в документе. Теперь рассмотрим другое регулярное выражение:
<[^>]*>Hello!
Как и раньше, он быстро совпадает с <
с >
, но не соответствует Hello!
. Он вернется к <
, затем закроется и начнет сканирование для другого <
. Он по-прежнему будет проверять каждый <
, как это сделал первое регулярное выражение, но он не будет искать весь путь до конца документа каждый раз, когда он найдет его.
Но это еще хуже. Если вы думаете об этом, .*?
фактически эквивалентен негативному взгляду. Он говорит: "Прежде чем использовать следующий символ, убедитесь, что остальная часть регулярного выражения не может совпадать в этой позиции". Другими словами,
/<.*?>Hello!/
... эквивалентно:
/<(?:(?!>Hello!).)*(?:>Hello!|\z(*FAIL))/
Итак, в каждой позиции, которую вы выполняете, а не только в обычном матче, но гораздо более дорогостоящем. (Это по крайней мере в два раза дороже, потому что просмотр должен сканировать хотя бы один символ, затем .
идет вперед и потребляет символ.)
((*FAIL)
является одним из Perl backtracking-control verbs (также поддерживается в PHP). |\z(*FAIL)
означает "или доходит до конца документа и отказаться".)
Наконец, есть еще одно преимущество подхода с отрицательным символом. Хотя это не так (как указывает @Bart), действуют так, как квантификатор является притяжательным, вам нечего мешать сделать его притяжательным, если ваш вкус поддерживает его:
/<[^>]*+>Hello!/
... или обернуть его в атомную группу:
/(?><[^>]*>)Hello!/
Эти регулярные выражения не только не возвращаются без необходимости, им не нужно сохранять информацию о состоянии, которая делает возможным откат.