- ru
- Language: en
- Documentation version: latest
Символы повторения
regex+
- одно или более повторений предшествующего элементаregex*
- ноль или более повторений предшествующего элементаregex?
- ноль или одно повторение предшествующего элементаregex{n}
- ровно n повторений предшествующего элементаregex{n,m}
- от n до m повторений предшествующего элементаregex{n, }
- n или более повторений предшествующего элемента
+
Плюс указывает, что предыдущее выражение может повторяться сколько угодно раз, но, как минимум, один раз.
Например, тут повторение относится к букве a:
In [1]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1'
In [2]: re.search('a+', line).group()
Out[2]: 'aa'
А в этом выражении повторяется строка ‘a1’:
In [3]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1'
In [4]: re.search('(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('\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('\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('ba*', line).group()
Out[10]: 'baaa'
Если в строке line до подстроки baaa встретится b, то совпадением будет b:
In [11]: line = '100 ab11.baaa.a5d3 FastEthernet0/1'
In [12]: re.search('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('\w+@\w+\.\w+', email1).group()
Out[14]: 'user1@gmail.com'
Но такое выражение не подходит для электронного адреса с точкой:
In [15]: email2 = 'user2.test@gmail.com'
In [16]: re.search('\w+@\w+\.\w+', email2).group()
Out[16]: 'test@gmail.com'
Регулярное выражение для адреса с точкой:
In [17]: re.search('\w+\.\w+@\w+\.\w+', email2).group()
Out[17]: 'user2.test@gmail.com'
Чтобы описать оба варианта адресов, надо указать, что точка в адресе опциональна:
'\w+\.*\w+@\w+\.\w+'
Такое регулярное выражение описывает оба варианта:
In [18]: email1 = 'user1@gmail.com'
In [19]: email2 = 'user2.test@gmail.com'
In [20]: re.search('\w+\.*\w+@\w+\.\w+', email1).group()
Out[20]: 'user1@gmail.com'
In [21]: re.search('\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('\w+\.?\w+@\w+\.\w+', message)
...: if match:
...: print("Found email: ", match.group())
...:
Found email: user1@gmail.com
Found email: user2.test@gmail.com
{n}
С помощью фигурных скобок можно указать, сколько раз должно повторяться предшествующее выражение.
Например, выражение \w{4}\.\w{4}\.\w{4}
описывает 12 букв или цифр,
которые разделены на три группы по четыре символа точками. Таким образом
можно получить MAC-адрес:
In [24]: line = '100 aab1.a1a1.a5d3 FastEthernet0/1'
In [25]: re.search('\w{4}\.\w{4}\.\w{4}', 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{1,4}
попадет номер VLAN:
In [27]: for line in mac_table.split('\n'):
...: match = re.search('\d{1,4}', line)
...: if match:
...: print('VLAN: ', match.group())
...:
VLAN: 1
VLAN: 100
VLAN: 200
VLAN: 300
VLAN: 1100
VLAN: 500
VLAN: 1200
VLAN: 1300
Выражение \d{1,4}
описывает от одной до четырех цифр.
Обратите внимание, что в выводе команды с оборудования нет VLAN с номером 1.
При этом регулярное выражение получило откуда-то число 1.
Цифра 1 попала в вывод из имени хоста в строке sw1#sh mac address-table
.
Чтобы исправить это, достаточно дополнить выражение и указать, что после цифр должен идти хотя бы один пробел:
In [28]: for line in mac_table.split('\n'):
...: match = re.search('\d{1,4} +', line)
...: if match:
...: print('VLAN: ', match.group())
...:
VLAN: 100
VLAN: 200
VLAN: 300
VLAN: 1100
VLAN: 500
VLAN: 1200
VLAN: 1300