- en
 - Language: ru
 
- Documentation version: latest
 
Функция findall
Функция findall():
используется для поиска всех непересекающихся совпадений в шаблоне
возвращает:
список строк, которые описаны регулярным выражением, если в регулярном выражении нет групп
список строк, которые совпали с регулярным выражением в группе, если в регулярном выражении одна группа
список кортежей, в которых находятся строки, которые совпали с выражением в группе, если групп несколько
Рассмотрим работу findall на примере вывода команды sh mac address-table:
In [2]: mac_address_table = open('CAM_table.txt').read()
In [3]: print(mac_address_table)
sw1#sh mac address-table
          Mac Address Table
-------------------------------------------
Vlan    Mac Address       Type        Ports
----    -----------       --------    -----
 100    a1b2.ac10.7000    DYNAMIC     Gi0/1
 200    a0d4.cb20.7000    DYNAMIC     Gi0/2
 300    acb4.cd30.7000    DYNAMIC     Gi0/3
 100    a2bb.ec40.7000    DYNAMIC     Gi0/4
 500    aa4b.c550.7000    DYNAMIC     Gi0/5
 200    a1bb.1c60.7000    DYNAMIC     Gi0/6
 300    aa0b.cc70.7000    DYNAMIC     Gi0/7
Первый пример - регулярное выражение без групп. В этом случае findall возвращает список строк, которые совпали с регулярным выражением.
Например, с помощью findall можно получить список строк с соответствиями vlan - mac - interface и избавиться от заголовка в выводе команды:
In [4]: re.findall(r'\d+ +\S+ +\w+ +\S+', mac_address_table)
Out[4]:
['100    a1b2.ac10.7000    DYNAMIC     Gi0/1',
 '200    a0d4.cb20.7000    DYNAMIC     Gi0/2',
 '300    acb4.cd30.7000    DYNAMIC     Gi0/3',
 '100    a2bb.ec40.7000    DYNAMIC     Gi0/4',
 '500    aa4b.c550.7000    DYNAMIC     Gi0/5',
 '200    a1bb.1c60.7000    DYNAMIC     Gi0/6',
 '300    aa0b.cc70.7000    DYNAMIC     Gi0/7']
Обратите внимание, что findall возвращает список строк, а не объект Match.
Как только в регулярном выражении появляется группа, findall ведет себя по-другому. Если в выражении используется одна группа, findall возвращает список строк, которые совпали с выражением в группе:
In [5]: re.findall(r'\d+ +(\S+) +\w+ +\S+', mac_address_table)
Out[5]:
['a1b2.ac10.7000',
 'a0d4.cb20.7000',
 'acb4.cd30.7000',
 'a2bb.ec40.7000',
 'aa4b.c550.7000',
 'a1bb.1c60.7000',
 'aa0b.cc70.7000']
При этом findall ищет совпадение всей строки, но возвращает результат, похожий на метод groups() в объекте Match.
Если же групп несколько, findall вернет список кортежей:
In [6]: re.findall(r'(\d+) +(\S+) +\w+ +(\S+)', mac_address_table)
Out[6]:
[('100', 'a1b2.ac10.7000', 'Gi0/1'),
 ('200', 'a0d4.cb20.7000', 'Gi0/2'),
 ('300', 'acb4.cd30.7000', 'Gi0/3'),
 ('100', 'a2bb.ec40.7000', 'Gi0/4'),
 ('500', 'aa4b.c550.7000', 'Gi0/5'),
 ('200', 'a1bb.1c60.7000', 'Gi0/6'),
 ('300', 'aa0b.cc70.7000', 'Gi0/7')]
Если такие особенности работы функции findall мешают получить необходимый результат, то лучше использовать функцию finditer, но иногда такое поведение подходит и удобно использовать.
Пример использования findall в разборе лог-файла (файл parse_log_findall.py):
import re
regex = (r'Host \S+ '
         r'in vlan (\d+) '
         r'is flapping between port '
         r'(\S+) and port (\S+)')
ports = set()
with open('log.txt') as f:
    result = re.findall(regex, f.read())
    for vlan, port1, port2 in result:
        ports.add(port1)
        ports.add(port2)
print('Петля между портами {} в VLAN {}'.format(', '.join(ports), vlan))
Результат:
$ python parse_log_findall.py
Петля между портами Gi0/19, Gi0/16, Gi0/24 в VLAN 10