Разница между re.findall() и re.finditer() при использовании групп в регулярном выражении?

#python #regex #text #regex-group

#python #регулярное выражение #текст #регулярное выражение-группа

Вопрос:

Рассмотрим следующую строку

 text2 = '''
Mr. Schafer
Mr Smith
Ms Davis
Mrs. Robinson
Mr. T
'''
  

Я хочу, чтобы регулярное выражение соответствовало полному имени, как в ‘Mr . Например, Schafer

Использование finditer():

 matches = re.finditer(r'(Mr|Ms|Mrs).?s[A-Z]w*', text2)
for match in matches:
    print(match)
  

Результаты:

 <_sre.SRE_Match object; span=(1, 12), match='Mr. Schafer'>
<_sre.SRE_Match object; span=(13, 21), match='Mr Smith'>
<_sre.SRE_Match object; span=(22, 30), match='Ms Davis'>
<_sre.SRE_Match object; span=(31, 44), match='Mrs. Robinson'>
<_sre.SRE_Match object; span=(45, 50), match='Mr. T'>
  

finditer() дает мне результаты, которые я хочу, но не в списке.

Но когда я использую findall():

 re.findall(r'(Mr|Ms|Mrs).?s[A-Z]w*', text2)
  

Результаты:

 ['Mr', 'Mr', 'Ms', 'Mrs', 'Mr']
  

Почему это так? Как я могу получить желаемый результат, используя findall() Я хочу
этот результат:

 ['Mr. Schafer', 'Mr Smith', 'Ms Davis', 'Mrs. Robinson', 'Mr. T']
  

Комментарии:

1. Вам нужно сделать группу без захвата: re.findall(r'(?:Mr|Ms|Mrs).?s[A-Z]w*', text2) , иначе она вернет захваченную группу вместо всего сопоставленного шаблона.

2. Я предпочитаю finditer over findall , поскольку iter не будет считывать все данные в ОЗУ, поскольку он возвращает итерацию, в то время как find all возвращает список. Чтобы получить значения, просто используйте .group() . Смотрите мой пример ниже.

Ответ №1:

Список, возвращаемый re.findall содержит:

  • текст каждого совпадения, если регулярное выражение не имеет захватов
  • текст захвата в каждом совпадении, если регулярное выражение имеет ровно один захват
  • кортеж подстрок, соответствующих каждому захвату, если регулярное выражение имеет более одного захвата.

Захват является частью регулярного выражения, заключенного в круглые скобки, если вы не используете (?:...) ; ?: в этом контексте указывает библиотеке регулярных выражений Python не рассматривать круглые скобки как определяющие захват. (Конечно, он все еще используется для группировки.)

Итак, самое простое (и, вероятно, самое быстрое) решение — убедиться, что в регулярном выражении нет захватов, используя (?:...) для окружения заголовка, а не просто (...) :

 >>> re.findall(r'(?:Mr|Ms|Mrs).?s[A-Z]w*', text2)
['Mr. Schafer', 'Mr Smith', 'Ms Davis', 'Mrs. Robinson', 'Mr. T']
  

Вы также можете явно записать полное имя:

 >>> re.findall(r'((?:Mr|Ms|Mrs).?s[A-Z]w*)', text2)
['Mr. Schafer', 'Mr Smith', 'Ms Davis', 'Mrs. Robinson', 'Mr. T']
  

В этом случае нет особого смысла делать это, но форма «один захват» может быть полезна, если вы хотите, чтобы часть шаблона не отображалась в выходных данных.

Наконец, вам может понадобиться как почетное имя, так и фамилия в кортеже:

 >>> re.findall(r'(?:(Mr|Ms|Mrs).?s([A-Z]w*))', text2)
[('Mr', 'Schafer'), ('Mr', 'Smith'), ('Ms', 'Davis'), ('Mrs', 'Robinson'), ('Mr', 'T')]
  

Ответ №2:

Часть «()» является индикатором захвата.

добавьте «?:», чтобы установить не-захват.

 import re

text2 = '''
        Mr. Schafer
        Mr Smith
        Ms Davis
        Mrs. Robinson
        Mr. T
        '''
print(re.findall(r"(?:Mr|Ms|Mrs).?s[A-Za-z]*w*", text2))
# ['Mr. Schafer', 'Mr Smith', 'Ms Davis', 'Mrs. Robinson', 'Mr. T']
  

https://regexr.com/
имеет чит-лист с левой стороны.

Ответ №3:

Я предпочитаю finditer over findall . finditer возвращает итератор совпадающих объектов в тексте, одновременно findall возвращая список совпадающих шаблонов в тексте. Для эффективности генераторы лучше, чем список, поскольку список всех считывает данные в память, а уровень — нет. Чтобы получить значения из iterator just use .group() .

 import re

text2 = '''
Mr. Schafer
Mr Smith
Ms Davis
Mrs. Robinson
Mr. T
'''


matches = re.finditer(r'(Mr|Ms|Mrs).?s[A-Z]w*', text2)

match_list = [match.group() for match in matches]
print(match_list)