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