re.findall ('(ab | cd)', строка) против re.findall ('(ab | cd) +', строка)

18

В регулярном выражении Python я сталкиваюсь с этой особой проблемой. Не могли бы вы дать инструкцию о различиях между re.findall('(ab|cd)', string)и re.findall('(ab|cd)+', string)?

import re

string = 'abcdla'
result = re.findall('(ab|cd)', string)
result2 = re.findall('(ab|cd)+', string)
print(result)
print(result2)

Фактический результат:

['ab', 'cd']
['cd']

Я не понимаю, почему второй результат также не содержит 'ab'?

Скала
источник
re.findall ('(ab | cd)', строка) получает ['ab', 'cd'] re.findall ('(ab | cd) +', строка) получает ['cd']
рок

Ответы:

15

+является повторным квантификатором, который соответствует одному или нескольким разам. В регулярном выражении (ab|cd)+вы повторяете группу захвата, (ab|cd) используя +. Это будет захватывать только последнюю итерацию.

Вы можете рассуждать об этом поведении следующим образом:

Скажите, что ваша строка abcdlaи регулярное выражение есть (ab|cd)+. Движок Regex найдет совпадение для группы между позициями 0 и 1 abи выйдет из группы захвата. Затем он видит +квантификатор и пытается снова захватить группу и захватывает cdмежду позициями 2 и 3.


Если вы хотите захватить все итерации, вы должны захватить повторяющуюся группу, с ((ab|cd)+)которой совпадает abcdи cd. Вы можете запретить захват внутренней группы, так как нам не нужны совпадения внутренней группы с ((?:ab|cd)+)какими совпадениямиabcd

https://www.regular-expressions.info/captureall.html

Из документов,

Допустим, вы хотите сопоставить тег как !abc!или !123!. Возможны только эти два, и вы хотите захватить abcили 123выяснить, какой тег вы получили. Это достаточно просто: !(abc|123)!сработает.

Теперь предположим, что тег может содержать несколько последовательностей abcи 123, например, !abc123!или !123abcabc!. Быстрое и простое решение !(abc|123)+!. Это регулярное выражение действительно будет соответствовать этим тегам. Однако это больше не отвечает нашему требованию о включении метки тега в группу захвата. Когда это регулярное выражение совпадает !abc123!, сохраняется только группа захвата 123. Когда это соответствует !123abcabc!, это только хранит abc.

Шашанк V
источник
Можете ли вы дать ссылку на какой-нибудь документ, разъясняющий тот факт, что + фиксирует только последнюю итерацию, и что такое группа захвата?
Гульзар
1
@Gulzar, обновил ответ. Вы можете прочитать о группах захвата здесь - регулярные выражения.info/refcapture.html
V
@ Шашанк, спасибо, твой ответ - именно то, что мне нужно. Искренне спасибо
рок
@rock Пожалуйста, примите ответ, если он решил ваш вопрос.
Шашанк V
Нет необходимости заключать все выражения в скобки. Просто '(?:ab|cd)+'будет работать.
Dukeling
5

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

   # group(0) return the matched string the captured groups are returned in groups or you can access them
   # using group(1), group(2).......  in your case there is only one group, one group will capture only 
   # one part so when you do this
   string = 'abcdla'
   print(re.match('(ab|cd)', string).group(0))  # only 'ab' is matched and the group will capture 'ab'
   print(re.match('(ab|cd)+', string).group(0)) # this will match 'abcd'  the group will capture only this part 'cd' the last iteration

findallсопоставить и использовать строку одновременно, давайте представим, что происходит с этим REGEX '(ab|cd)':

      'abcdabla' ---> 1:   match: 'ab' |  capture : ab  | left to process:  'cdabla'
      'cdabla'   ---> 2:   match: 'cd' |  capture : cd  | left to process:  'abla'
      'abla'     ---> 3:   match: 'ab' |  capture : ab  | left to process:  'la'
      'la'       ---> 4:   match: '' |  capture : None  | left to process:  ''

      --- final : result captured ['ab', 'cd', 'ab']  

Теперь то же самое с '(ab|cd)+'

      'abcdabla' ---> 1:   match: 'abcdab' |  capture : 'ab'  | left to process:  'la'
      'la'       ---> 2:   match: '' |  capture : None  | left to process:  ''
      ---> final result :   ['ab']  

Я надеюсь, что это немного прояснит ситуацию.

Charif DZ
источник
0

Итак, для меня запутанной частью был тот факт, что

Если в шаблоне присутствует одна или несколько групп, вернуть список групп;

документы

так что он возвращает вам не полное совпадение, а только совпадение захвата. Если вы сделаете эту группу не захватывающей (re.findall('(?:ab|cd)+', string), она вернется, ["abcd"]как я и ожидал

Риад
источник
не уверен, что вы тоже ожидали или нет
RiaD