re.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('\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('\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('(\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 = ('Host \S+ ' 'in vlan (\d+) ' 'is flapping between port ' '(\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