Группировка выражений#
Группировка выражений указывает, что последовательность символов надо рассматривать как одно целое. Однако это не единственное преимущество группировки.
Кроме этого, с помощью групп можно получать только определенную часть строки, которая была описана выражением. Это очень полезно в ситуациях, когда надо описать строку достаточно подробно, чтобы отобрать нужные строки, но в то же время из самой строки надо получить только определенное значение.
Например, из log-файла надо отобрать строки, в которых встречается «%SW_MATM-4-MACFLAP_NOTIF», а затем из каждой такой строки получить MAC-адрес, VLAN и интерфейсы. В этом случае регулярное выражение просто должно описывать строку, а все части строки, которые надо получить в результате, просто заключаются в скобки.
В Python есть два варианта использования групп:
Нумерованные группы#
Группа определяется помещением выражения в круглые скобки () .
Внутри выражения группы нумеруются слева направо, начиная с 1. Затем к группам можно обращаться по номерам и получать текст, который соответствует выражению в группе.
Пример использования групп:
In [8]: line = "FastEthernet0/1 10.0.12.1 YES manual up up" In [9]: match = re.search(r'(\S+)\s+([\w.]+)\s+.*', line)
В данном примере указаны две группы:
- первая группа — любые символы, кроме пробельных
- вторая группа — любая буква или цифра (символ \w ) или точка
Вторую группу можно было описать так же, как и первую. Другой вариант сделан просто для примера
Теперь можно обращаться к группам по номеру. Группа 0 — это строка, которая соответствует всему шаблону:
In [10]: match.group(0) Out[10]: 'FastEthernet0/1 10.0.12.1 YES manual up up' In [11]: match.group(1) Out[11]: 'FastEthernet0/1' In [12]: match.group(2) Out[12]: '10.0.12.1'
При необходимости можно перечислить несколько номеров групп:
In [13]: match.group(1, 2) Out[13]: ('FastEthernet0/1', '10.0.12.1') In [14]: match.group(2, 1, 2) Out[14]: ('10.0.12.1', 'FastEthernet0/1', '10.0.12.1')
Начиная с версии Python 3.6, к группам можно обращаться таким образом:
In [15]: match[0] Out[15]: 'FastEthernet0/1 10.0.12.1 YES manual up up' In [16]: match[1] Out[16]: 'FastEthernet0/1' In [17]: match[2] Out[17]: '10.0.12.1'
Для вывода всех подстрок, которые соответствуют указанным группам, используется метод groups:
In [18]: match.groups() Out[18]: ('FastEthernet0/1', '10.0.12.1')
Именованные группы#
Когда выражение сложное, не очень удобно определять номер группы. Плюс, при дополнении выражения, может получиться так, что порядок групп изменился, и придется изменить и код, который ссылается на группы.
Именованные группы позволяют задавать группе имя.
Синтаксис именованной группы (?Pregex) :
In [19]: line = "FastEthernet0/1 10.0.12.1 YES manual up up" In [20]: match = re.search(r'(?P\S+)\s+(?P[\d.]+)\s+', line)
Теперь к этим группам можно обращаться по имени:
In [21]: match.group('intf') Out[21]: 'FastEthernet0/1' In [22]: match.group('address') Out[22]: '10.0.12.1'
Также очень полезно то, что с помощью метода groupdict(), можно получить словарь, где ключи — имена групп, а значения — подстроки, которые им соответствуют:
In [23]: match.groupdict() Out[23]: 'address': '10.0.12.1', 'intf': 'FastEthernet0/1'>
И в таком случае можно добавить группы в регулярное выражение и полагаться на их имя, а не на порядок:
In [24]: match = re.search(r'(?P\S+)\s+(?P[\d\.]+)\s+\w+\s+\w+\s+(?Pup|down)\s+(?Pup|down)', line) In [25]: match.groupdict() Out[25]: 'address': '10.0.12.1', 'intf': 'FastEthernet0/1', 'protocol': 'up', 'status': 'up'>