2016-02-21 3 views
2

Недавно я играл с выражениями регулярных выражений в Python и столкнулся с проблемой с r"(\w{3})+" и с ее не-жадным эквивалентом r"(\w{3})+?". Пожалуйста, давайте рассмотрим следующий пример:Захват групп и жадность в Python

S = "abcdefghi" # string used for all the cases below 

1. Жадный поиск

m = re.search(r"(\w{3})+", S) 
print m.group() # abcdefghi 
print m.groups() # ('ghi',) 

m.group точно так, как я ожидал - только весь матч.

Относительно m.groups пожалуйста, проверьте: ghi печатается, так как он перезаписывается предыдущие захвачена группы def и abc, я прав? Если да, то могу ли я захватить все перезаписанные группы? Конечно, для этой конкретной строки я мог бы просто написать m = re.search(r"(\w{3})(\w{3})(\w{3})", S), но я ищу более общий способ захвата групп, не зная, сколько из них я могу ожидать, таким образом, metacharacter +.

2. Non-жадный поиск

m = re.search(r"(\w{3})+?", S) 
print m.group() # abc 
print m.groups() # ('abc',) 

Теперь мы не жадные так был найден только abc - точно так, как я ожидал.

Относительно m.groups() двигатель остановился, когда он нашел abc, поэтому я понимаю, что это единственная найденная группа здесь.

3. Жадный FindAll

print re.findall(r"(\w{3})+", S) # ['ghi'] 

Теперь я действительно озадачен, я всегда считал, что функция re.findall находит все подстроки, где RE спичек и возвращает их в виде списка. Здесь у нас есть только одно совпадение abcdefghi (в соответствии с здравым смыслом и пулей), поэтому я ожидал наличия списка, содержащего этот элемент. Почему только ghi был возвращен?

4. Non-жадный FindAll

print re.findall(r"(\w{3})+?", S) # ['abc', 'def', 'ghi'] 

Здесь, в свою очередь, я ожидал иметь abc только, но, возможно, имея пулю объяснена поможет мне понять это, как хорошо. Может быть, это даже ответ на мой вопрос от bullet (о захвате перезаписанных групп), но я бы очень хотел понять, что здесь происходит.

ответ

1

Вы должны думать о жадном/нежадным поведении в контексте вашего регулярного выражения (r"(\w{3})+") по сравнению с регулярным выражением, где повторяющийся узор не был в конце: (r"(\w{3})+\w")

Это важно, потому что поведение соответствия регулярных выражений по умолчанию:

  1. весь регулярное выражение должно соответствовать
  2. Начиная уже в целевой строке, как это возможно
  3. Matching как большая часть целевой строки, насколько это возможно (жадный)

Если у вас есть «повторить» оператор - либо * или + - в своем регулярном выражении, то поведение по умолчанию для этого, чтобы соответствовать, как много как можно, до тех пор, пока остальное регулярное выражение выполнено.

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

Если у вас есть повторный оператор с не жадным классификатором - *? или +? - в своем регулярном выражении, то поведение, чтобы соответствовать, как мало как он может, так долго, как остальная часть регулярного выражения выполняется ,

Когда повтор-nongreedy оператор в конце узора, нет остальной части регулярного выражения, так что поведение становится матч так же мало, как это возможно.

Все, что находится в Всего один матч.Вы также смешиваете re.findall(), который затем повторяет матч, если это возможно.

При первом запуске re.findall, с r"(\w{3})+" вы используете жадный матч в конце рисунка. Таким образом, он попытается применить этот последний блок как много раз, сколько возможно в одном матче. У вас есть случай, когда, как и призыв к re.search, единственное соответствие потребляет всю строку. Как часть потребления всей строки, блок w3 повторяется, и групповой буфер перезаписывается несколько раз.

Во второй раз, когда вы запускаете re.findall, с r"(\w{3})+?" вы используете не жадное соответствие в конце рисунка. Таким образом, он попытается применить этот последний блок как несколько раз по возможности в одном матче. Поскольку оператор равен +, это будет 1. Теперь у вас есть случай, когда совпадение может прекратиться, не потребляя всю строку. И теперь групповой буфер заполняется только один раз, а не перезаписывается. Это означает, что findall может вернуть этот результат (abc), затем цикл для другого результата (def), затем цикл для конечного результата (ghi).

1

Относительно m.groups пожалуйста, проверьте: ghi печатается, так как он перезаписывается ранее захваченные группы def и abc, я прав?

  • Право. Только последний записанный текст сохраняется в буфере групповой памяти.

Можно ли захватить все переписанные группы?

  • Не с re, но с PyPi regex, вы можете. Его объект совпадения имеет метод captures. Однако, с re, вы можете просто сравнить их с re.findall(r'\w{3}', S). Однако в этом случае вы будете сопоставлять все фрагменты символов из 3-х слов из строки, а не только из последовательных. С помощью модуля регулярных выражений вы можете получить все 3-символьные последовательные фрагменты с начала строки с помощью оператора \G: regex.findall(r"\G\w{3}", "abcdefghi") (результат: , def, ghi).

Почему только ghi был возвращен с re.findall(r"(\w{3})+", S)?

  • Потому что есть только один матч, который равен всей abcdefghi строки, и захват группы 1 содержит только последние три символа. re.findall возвращает только полученные значения, если группы захвата определены в шаблоне.
+0

Если что-то неясно, вы можете оставить комментарий здесь, и я буду обновлять ответ с соответствующими сведениями. –

+0

Этот вопрос по-прежнему актуальный, и, конечно же, я теперь отдает должное тем людям, которые бескорыстно помогают другим. Моя задержка возникает из-за того, что я пытаюсь выяснить, насколько могу, самостоятельно и не беспокоить, чтобы помогать людям с множеством вопросов - эти два ответа были изначально непонятными и для моих вчерашних знаний требовалось много исследований , Во всяком случае, кажется, что мои намерения истолковываются негативно. Я начинаю знакомство с этим сайтом и не знаю его обычаев - если не будет неприятно задавать вопросы сразу, это здорово. – Drizzt

+0

Это сайт вопросов и ответов. Вопросы должны быть о программировании: у вас есть задача, вы работаете над ней, застреваете с какой-то частью кода, вы просите о помощи, так как вы пробовали то или это, и это не работает. Подробнее в [Как спросить] (http://stackoverflow.com/help/how-to-ask). Этот ваш вопрос - это вопрос, который время от времени случается, но у вас действительно есть проблемы с пониманием основных вещей. Вы можете многому научиться на [regular-expressions.info] (http://regular-expressions.info). –

Смежные вопросы