В чем разница между.*? и. * регулярные выражения?
Я пытаюсь разделить строку на две части с помощью регулярных выражений. Строка отформатирована следующим образом:
text to extract<number>
Я использую (.*?)<
а также <(.*?)>
которые работают хорошо, но после прочтения в регулярное выражение, я только начал задаваться вопросом, зачем мне ?
в выражениях. Я сделал это только после того, как нашел их на этом сайте, поэтому я не совсем уверен, в чем разница.
3 ответа
Это разница между жадными и не жадными квантификаторами.
Рассмотрим вход 101000000000100
,
С помощью 1.*1
, *
является жадным - он будет соответствовать полностью до конца, а затем возвращаться, пока не сможет соответствовать 1
, оставляя тебя с 1010000000001
,.*?
не жадный *
ничего не будет соответствовать, но затем будет пытаться сопоставить дополнительные символы, пока не совпадет 1
, в конце концов, соответствие 101
,
Все квантификаторы имеют не жадный режим: .*?
, .+?
, .{2,6}?
, и даже .??
,
В вашем случае аналогичная картина может быть <([^>]*)>
- сопоставление чего-либо, кроме знака "больше чем" (строго говоря, оно соответствует нулю или большему числу символов, чем >
между <
а также >
).
На жадных против не жадных
Повторение в регулярном выражении по умолчанию является жадным: они пытаются сопоставить столько повторений, сколько возможно, и когда это не работает, и им приходится возвращаться назад, они пытаются сопоставить одно меньшее количество повторений за раз, пока совпадение всего шаблона не будет найденный. В результате, когда совпадение, наконец, происходит, жадное повторение будет соответствовать как можно большему количеству повторений.
?
поскольку квантификатор повторения меняет это поведение на нежадное, также называемое неохотным ( например, в Java) (а иногда и "ленивым"). Напротив, это повторение сначала будет пытаться сопоставить как можно меньше повторений, и когда это не сработает, и они должны вернуться назад, они начинают сопоставлять еще одно повторение за раз. В результате, когда совпадение, наконец, происходит, неохотное повторение будет соответствовать как можно меньшему количеству повторений.
Рекомендации
Пример 1: от А до Я
Давайте сравним эти две модели: A.*Z
а также A.*?Z
,
Учитывая следующий вход:
eeeAiiZuuuuAoooZeeee
Шаблоны дают следующие совпадения:
A.*Z
дает 1 матч:AiiZuuuuAoooZ
( см. на rubular.com)A.*?Z
дает 2 матча:AiiZ
а такжеAoooZ
( см. на rubular.com)
Давайте сначала сосредоточимся на том, что 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
,
Основное различие между двумя шаблонами заключается в производительности: будучи более строгим, класс отрицанных символов может соответствовать только одному способу для данного ввода. Не имеет значения, используете ли вы жадный или неохотный модификатор для этого шаблона. На самом деле, в некоторых вариантах вы можете сделать еще лучше и использовать так называемый притяжательный квантификатор, который вообще не возвращается.
Рекомендации
- регулярно-expressions.info/Repetition - альтернатива лени, отрицательных классов символов и позитивных квантификаторов
Пример 2: от А до Я.
Этот пример должен быть иллюстративным: он показывает, как шаблоны классов жадных, неохотных и отрицательных символов по-разному совпадают при одном и том же вводе.
eeAiiZooAuuZZeeeZZfff
Это совпадения для вышеуказанного ввода:
A[^Z]*ZZ
дает 1 матч:AuuZZ
( как видно на ideone.com)A.*?ZZ
дает 1 матч:AiiZooAuuZZ
( как видно на ideone.com)A.*ZZ
дает 1 матч:AiiZooAuuZZeeeZZ
( как видно на ideone.com)
Вот визуальное представление того, что они совпали:
___n
/ \ n = negated character class
eeAiiZooAuuZZeeeZZfff r = reluctant
\_________/r / g = greedy
\____________/g
похожие темы
Это ссылки на вопросы и ответы по stackru, которые охватывают некоторые темы, которые могут представлять интерес.
Одно жадное повторение может перерасти другое
Допустим, у вас есть:
<a></a>
<(.*)>
будет соответствовать a></a
в то время как <(.*?)>
будет соответствовать a
, Последний останавливается после первого матча >
, Он проверяет на одно или 0 совпадений .*
с последующим следующим выражением.
Первое выражение <(.*)>
не останавливается при сопоставлении первого >
, Это будет продолжаться до последнего матча >
,