Почему на минимальное (не жадное) совпадение влияет конец строкового символа '$'? - PullRequest
7 голосов
/ 04 мая 2011

РЕДАКТИРОВАТЬ: удалить оригинальный пример, потому что это вызвало вспомогательные ответы.также исправил заголовок.

Вопрос в том, почему присутствие "$" в регулярном выражении влияет на жадность выражения:

Вот более простой пример:

>>> import re
>>> str = "baaaaaaaa"
>>> m = re.search(r"a+$", str)
>>> m.group()
'aaaaaaaa'
>>> m = re.search(r"a+?$", str)
>>> m.group()
'aaaaaaaa'

"?"кажется, ничего не делает.Обратите внимание, что когда удаляется «$», тогда «?»уважается:

>>> m = re.search(r"a+?", str)
>>> m.group()
'a'

РЕДАКТИРОВАТЬ: Другими словами, «+ + $» соответствует ВСЕМ а, а не только последнему, это не то, что я ожидал.Вот описание регулярного выражения "+?"из документов Python : "Добавление '?"после того, как квалификатор заставит его выполнить сопоставление не жадным или минимальным образом; будет сопоставлено как можно меньше символов. "

В данном примере это не так: строка" a "соответствуетрегулярное выражение "a +? $", так почему же совпадение для одного и того же регулярного выражения в строке "baaaaaaa" не является единственным a (самый правый)?

Ответы [ 6 ]

4 голосов
/ 04 мая 2011

Модификатор отсутствия жадности влияет только на то место, где останавливается , а не там, где начинается . Если вы хотите начать матч как можно позже, вам нужно будет добавить .+? в начало вашего паттерна.

Без $ ваш шаблон позволяет быть менее жадным и останавливаться раньше, потому что он не должен совпадать с концом строки.

EDIT:

Подробнее ... В этом случае:

re.search(r"a+?$", "baaaaaaaa")

движок регулярных выражений будет игнорировать все до первого «а», потому что именно так работает re.search. Он будет соответствовать первому a и "захочет" вернуть совпадение, за исключением того, что он еще не соответствует шаблону, поскольку должен достичь совпадения для $. Так что он просто продолжает есть a по одному и проверять $. Если бы он был жадным, он не проверял бы $ после каждого a, но только после того, как он больше не мог соответствовать a.

Но в этом случае:

re.search(r"a+?", "baaaaaaaa")

Движок регулярных выражений проверяет, есть ли у него полное совпадение после употребления первого совпадения (потому что оно не жадное), и успешно , потому что в этом случае $ нет.

4 голосов
/ 04 мая 2011

Совпадения "упорядочены" по "крайний левый, затем самый длинный" ;однако «самый длинный» - это термин, который использовался до того, как было разрешено не жадное обращение, а вместо этого означает что-то вроде «предпочтительного числа повторений для каждого атома».Быть самым левым на важнее , чем количество повторений.Таким образом, «a +? $» Не будет соответствовать последнему A в «baaaaa», поскольку сопоставление с первым A начинается раньше в строке.

(Ответ изменен после пояснения OP в комментариях. См. Историю в предыдущем тексте.)

3 голосов
/ 06 мая 2011

Наличие $ в регулярном выражении не влияет на жадность выражения. Это просто добавляет другое условие, которое должно быть выполнено, чтобы общий матч был успешным.

И a+, и a+? должны потреблять первые найденные a. Если за этим a следуют еще a, a+ идет вперед и потребляет их тоже, в то время как a+? доволен только одним. Если бы в регулярном выражении было что-то большее, a+ был бы готов согласиться на меньшее количество a, а a+? потребляло бы больше, если это то, что требовалось для достижения соответствия.

С a+$ и a+?$ вы добавили еще одно условие: сопоставьте хотя бы одно a , за которым следует конец строки. a+ по-прежнему потребляет все a вначале, а затем передает его на якорь ($). Это удастся с первой попытки, поэтому a+ не нужно возвращать ни одного из a.

С другой стороны, a+? изначально потребляет только один a, прежде чем передать $. Это терпит неудачу, поэтому элемент управления возвращается к a+?, который потребляет еще a и снова отключается. И так до тех пор, пока a+? не израсходует последние a и $ наконец не преуспеет. Так что да, a+?$ соответствует тому же числу a, что и a+$, но это происходит неохотно, но неохотно.

Что касается самого левого-самого длинного правила, которое было упомянуто в другом месте, оно никогда не применялось к производным от Perl разновидностям регулярных выражений, таким как Python. Даже без неохотных квантификаторов они всегда могут вернуть совпадение меньше максимального, благодаря упорядоченному чередованию . Я думаю, что у Джена правильная идея: Perl-производные (или ориентированные на регулярные выражения) ароматы должны называться eager , а не жадными.

Я считаю, что самое левое-самое длинное правило применимо только к регулярным выражениям POSIX NFA, которые используют механизмы NFA под капотом, но обязаны возвращать те же результаты, что и регулярное выражение DFA (текстовое).

1 голос
/ 04 мая 2011

Ответ на оригинальный вопрос:

Почему первый поиск () охватывает несколько "/", а не принимая кратчайший матч?

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

Ответ на пересмотренный вопрос:

Нежадный подшаблон примет самое короткое совпадение , соответствующее всей последовательности, следующей за .

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

1 голос
/ 04 мая 2011

Здесь происходит две проблемы. Вы использовали group () без указания группы, и я могу сказать, что вы путаетесь между поведением регулярных выражений с явно заключенной в скобки группой и без a заключенная в скобки группа. Такое поведение без круглых скобок, которое вы наблюдаете, является просто ярлыком, который предоставляет Python, и вам нужно прочитать документацию по group () , чтобы понять ее полностью.

>>> import re
>>> string = "baaa"
>>> 
>>> # Here you're searching for one or more `a`s until the end of the line.
>>> pattern = re.search(r"a+$", string)
>>> pattern.group()
'aaa'
>>> 
>>> # This means the same thing as above, since the presence of the `$`
>>> # cancels out any meaning that the `?` might have.
>>> pattern = re.search(r"a+?$", string)
>>> pattern.group()
'aaa'
>>> 
>>> # Here you remove the `$`, so it matches the least amount of `a` it can.
>>> pattern = re.search(r"a+?", string)
>>> pattern.group()
'a'

Суть в том, что строка a+? соответствует одному a, точка. Однако a+?$ соответствует a до конца строки . Обратите внимание, что без явной группировки вам будет трудно заставить ? вообще что-либо значить. В общем, в любом случае лучше четко указывать, что вы группируете с помощью скобок. Позвольте мне привести пример с явными группами.

>>> # This is close to the example pattern with `a+?$` and therefore `a+$`.
>>> # It matches `a`s until the end of the line. Again the `?` can't do anything.
>>> pattern = re.search(r"(a+?)$", string)
>>> pattern.group(1)
'aaa'
>>>
>>> # In order to get the `?` to work, you need something else in your pattern
>>> # and outside your group that can be matched that will allow the selection
>>> # of `a`s to be lazy. # In this case, the `.*` is greedy and will gobble up
>>> # everything that the lazy `a+?` doesn't want to.
>>> pattern = re.search(r"(a+?).*$", string)
>>> pattern.group(1)
'a'

Редактировать: Удален текст, связанный со старыми версиями вопроса.

0 голосов
/ 04 мая 2011

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

>>> import os
>>> p = "/we/shant/see/this/butshouldseethis"
>>> os.path.basename(p)
butshouldseethis
...