Символы повторения#
Плюс указывает, что предыдущее выражение может повторяться сколько угодно раз, но, как минимум, один раз.
Например, тут повторение относится к букве a:
In [1]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1' In [2]: re.search(r'a+', line).group() Out[2]: 'aa'
А в этом выражении повторяется строка „a1“:
In [3]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1' In [4]: re.search(r'(a1)+', line).group() Out[4]: 'a1a1' В выражении ``(a1)+`` скобки используются для того, чтобы указать, что повторение относится к последовательности символов 'a1'.
IP-адрес можно описать выражением \d+\.\d+\.\d+\.\d+ . Тут плюс используется, чтобы указать, что цифр может быть несколько. А также встречается выражение \. .
Оно необходимо из-за того, что точка является специальным символом (она обозначает любой символ). И чтобы указать, что нас интересует именно точка, надо ее экранировать — поместить перед точкой обратный слеш.
Используя это выражение, можно получить IP-адрес из строки sh_ip_int_br:
In [5]: sh_ip_int_br = 'Ethernet0/1 192.168.200.1 YES NVRAM up up' In [6]: re.search(r'\d+\.\d+\.\d+\.\d+', sh_ip_int_br).group() Out[6]: '192.168.200.1'
Еще один пример выражения: \d+\s+\S+ — оно описывает строку, в которой идут сначала цифры, после них пробельные символы, а затем непробельные символы (все, кроме пробела, таба и других подобных символов). С его помощью можно получить VLAN и MAC-адрес из строки:
In [7]: line = '1500 aab1.a1a1.a5d3 FastEthernet0/1' In [8]: re.search(r'\d+\s+\S+', line).group() Out[8]: '1500 aab1.a1a1.a5d3'
Звездочка указывает, что предыдущее выражение может повторяться 0 или более раз.
Например, если звездочка стоит после символа, она означает повторение этого символа.
Выражение ba* означает b, а затем ноль или более повторений a:
In [9]: line = '100 a011.baaa.a5d3 FastEthernet0/1' In [10]: re.search(r'ba*', line).group() Out[10]: 'baaa'
Если в строке line до подстроки baaa встретится b, то совпадением будет b:
In [11]: line = '100 ab11.baaa.a5d3 FastEthernet0/1' In [12]: re.search(r'ba*', line).group() Out[12]: 'b'
Допустим, необходимо написать регулярное выражение, которое описывает электронные адреса в двух форматах: user @ example . com и user . test @ example . com. То есть, в левой части адреса может быть или одно слово, или два слова, разделенные точкой.
Первый вариант на примере адреса без точки:
In [13]: email1 = 'user1@gmail.com'
Этот адрес можно описать таким выражением \w+@\w+\.\w+ :
In [14]: re.search(r'\w+@\w+\.\w+', email1).group() Out[14]: 'user1@gmail.com'
Но такое выражение не подходит для электронного адреса с точкой:
In [15]: email2 = 'user2.test@gmail.com' In [16]: re.search(r'\w+@\w+\.\w+', email2).group() Out[16]: 'test@gmail.com'
Регулярное выражение для адреса с точкой:
In [17]: re.search(r'\w+\.\w+@\w+\.\w+', email2).group() Out[17]: 'user2.test@gmail.com'
Чтобы описать оба варианта адресов, надо указать, что точка в адресе опциональна:
Такое регулярное выражение описывает оба варианта:
In [18]: email1 = 'user1@gmail.com' In [19]: email2 = 'user2.test@gmail.com' In [20]: re.search(r'\w+\.*\w+@\w+\.\w+', email1).group() Out[20]: 'user1@gmail.com' In [21]: re.search(r'\w+\.*\w+@\w+\.\w+', email2).group() Out[21]: 'user2.test@gmail.com'
В последнем примере регулярное выражение указывает, что точка необязательна, но в то же время определяет, что она может появиться много раз.
В этой ситуации логичней использовать знак вопроса. Он обозначает ноль или одно повторение предыдущего выражения или символа. Теперь регулярное выражение выглядит так \w+\.?\w+@\w+\.\w+ :
In [22]: mail_log = ['Jun 18 14:10:35 client-ip=154.10.180.10 from=user1@gmail.com, size=551', . : 'Jun 18 14:11:05 client-ip=150.10.180.10 from=user2.test@gmail.com, size=768'] In [23]: for message in mail_log: . : match = re.search(r'\w+\.?\w+@\w+\.\w+', message) . : if match: . : print("Found email: ", match.group()) . : Found email: user1@gmail.com Found email: user2.test@gmail.com
С помощью фигурных скобок можно указать, сколько раз должно повторяться предшествующее выражение.
Например, выражение \w\.\w\.\w описывает 12 букв или цифр, которые разделены на три группы по четыре символа точками. Таким образом можно получить MAC-адрес:
In [24]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1' In [25]: re.search(r'\w \.\w \.\w ', line).group() Out[25]: 'aab1.a1a1.a5d3'
В фигурных скобках можно указывать и диапазон повторений. Например, попробуем получить все номера VLAN из строки mac_table:
In [26]: mac_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 . 1100 a2bb.ec40.7000 DYNAMIC Gi0/4 . 500 aa4b.c550.7000 DYNAMIC Gi0/5 . 1200 a1bb.1c60.7000 DYNAMIC Gi0/6 . 1300 aa0b.cc70.7000 DYNAMIC Gi0/7 . '''
Так как search ищет только первое совпадение, в выражение \d попадет номер VLAN:
In [27]: for line in mac_table.split('\n'): . : match = re.search(r'\d', line) . : if match: . : print('VLAN: ', match.group()) . : VLAN: 1 VLAN: 100 VLAN: 200 VLAN: 300 VLAN: 1100 VLAN: 500 VLAN: 1200 VLAN: 1300
Выражение \d описывает от одной до четырех цифр.
Обратите внимание, что в выводе команды с оборудования нет VLAN с номером 1. При этом регулярное выражение получило откуда-то число 1. Цифра 1 попала в вывод из имени хоста в строке sw1#sh mac address-table .
Чтобы исправить это, достаточно дополнить выражение и указать, что после цифр должен идти хотя бы один пробел:
In [28]: for line in mac_table.split('\n'): . : match = re.search(r'\d +', line) . : if match: . : print('VLAN: ', match.group()) . : VLAN: 100 VLAN: 200 VLAN: 300 VLAN: 1100 VLAN: 500 VLAN: 1200 VLAN: 1300