Учебная страница курса биоинформатики,
год поступления 2017
Указания к заданию 11
Регулярные выражения
Часто возникает необходимость проверить, является ли данная строка корректным именем пользователя, последовательности и тд.
Кроме того, вы можете точно не знать, как пишется авторами искомое вами слово. К примеру, для гемоглобина используется как слово hemoglobin, так и слово haemoglobin (и еще пара грамматически не очень правильных вариантов)
Можно, конечно, сделать список со всеми вариантами написания искомого слова.
Во-первых, искать каждое слово в строке по очереди – медленно, а во-вторых, если нужно выделить группу слов и для каждого из них есть разные написания, некоторые слова могут не встречаться в группе и т.д., то такой подход превращается в сущий ад.
Для того, чтобы избежать этой проблемы (и создать новую) и созданы регулярные выражения.
Регулярное выражение — это последовательность символов(не меняют своего значения) и спецсимволов (имеют специальный смысл в пределах регулярного выражения), описывающая какую-то группу слов.
Правила составления регулярного выражения
- Большинство букв и символов соответствуют сами себе. Например, регулярное выражение "test" будет в точности соответствовать строке "test".
Исключения: - . ^ $ * + ? { [ ] \ | ( )
- Точка соответствует любому символу.
[любые_обычные_символы] будет соответствовать любому из перечисленных в квадратных скобочках символу. Например, [aAb] соответствует a или A или b.
- Если нужно указать диапазон символов (цифры от 0 до 5, например), используется выражение вида [0-5]. При этом заглавные и строчные буквы идут отдельно, то есть, например, [A-Z] соответсвует любой заглавной букве, но никакой из строчных.
- Шапочка ^ имеет два значения:
Отрицание. Валидно только В НАЧАЛЕ пары квадратных скобок. То есть [^0] — любой символ, кроме 0.
- Начало строки в начале регулярного выражения. "^Петя" будет соответствовать слову "Петя" только в начале строки.
- Знак доллара $ означает конец строки: "Петя$" будет соответствовать слову "Петя" только в конце строки.
- Обратная косая черта \ используется для экранирования спецсимвола, то есть в случае спецсимволов он превращает их в обычные. При этом некоторые комбинации её с обычными обозначают определенные множества:
- \d Соответствует любой цифре; эквивалент класса [0-9].
- \D Соответствует любому нечисловому символу; эквивалент класса [^0-9].
- \t (внезапно!) обозначает табулятор.
- \s Соответствует любому символу из класса whitespace ("невидимому"), в том числе пробелу и табулятору.
- \S Соответствует любому не-whitespace (то есть видимому) символу.
\w Соответствует любой букве или цифре, к которым затесалось нижнее подчёркивание; эквивалент [a-zA-Z0-9_].
\W Наоборот; эквивалент [^a-zA-Z0-9_].
- Круглые скобки выделяют группу символов. В выражении "P(eti)a" выделена отдельная группа "eti"; позже будет понятно, зачем это сделано.
- +, располагающийся после любого символа, множества [] или группы символов () означает, что символ/элементы из множества/группа символов должны встречаться не менее чем один раз.
- Аналогично, ? означает, что символ или группа может встретиться 0 или 1 раз.
- Звезда * означает, что символ или группа может встретиться любое число раз, включая 0. Например, a.*b соответствует любому слову, начинающемуся с a и заканчивающемуся b.
- Обобщением всего этого является {min, max}, расположенный также после любого символа, множество [] или группы символов () и значащий, что они должны встречаться не менее min раз и не более max раз. То есть ? эквивалентен {0,1}, + эквивалентен {1,} и * эквивалентна {0,}.
Вертикальная черта | означает "или": "group1|group2" означает, что должна встретиться либо первая группа, либо вторая.
Как искать с помощью такого способа записи
В Python есть специальный модуль для работы с регулярными выражениями - re.
Основными функциями данного модуля являются функции re.match, re.search, re.finditer и re.findall
Метод/атрибут |
Цель |
match(regex, string) |
Определить, начинается ли совпадение регулярного выражения с начала строки |
search(regex, string) |
Сканировать всю строку в поисках всех мест совпадений с регулярным выражением |
findall(regex, string) |
Найти все подстроки совпадений с регулярным выражением и вернуть их в виде списка |
finditer(regex, string) |
Найти все подстроки совпадений с регулярным выражением и вернуть их в виде объекта, по которому можно пройтись циклом for |
Отдельным особняком стоит re.split(regex, string), позволяющая делить строку по вхождению регулярного выражения
Примеры
Здесь и далее будем использовать re.match, как наиболее простую функцию. В случае, если вхождение регулярного выражения начинается с начала строки, она возвращает "_sre.SRE_Match object" - объект в Python, для которого вам надо знать только функцию group(), возвращающую вхождение всего регулярного выражения. Если же вхождения в начале нет. то возвращается None. У него нет метода group, потому обращение к нему вызовет ошибку.
1. Напишем регулярное выражение, соответствующее трем подряд идущем цифрам
Очевидно, можно написать проще
2. Напишем регулярное выражение, соответствующее записи имени человека в формате "Фамилия Имя (Отчество)", т.е отчество может быть указано, а может не быть Здесь мы говорим, что Фамилия и Имя должны начинаться с заглавной буквы и иметь минимум два символа, а Отчество может быть, а может не быть (для этого группа символов, соответствующая отчеству, выделена в группу)
1 import re
2 regular = "[A-Z][a-z]+ [A-Z][a-z]+ ([A-Z][a-z]+)?"
3 pat = re.match(regular, "Penzar Dmitry")
4 print pat.group() # "Penzar Dmitry"
5 pat = re.match(regular, "Penzar Dmitry Dmitrievich")
6 print pat.group() # "Penzar Dmitry Dmitrievich"
7 pat = re.match(regular, "Dmitry")
8 print pat # None
9 pat = re.match(regular, "Dmitry 89175296136")
10 print pat # None
3. Напишем регулярное выражение, принимающее как домашний телефон (XXX-XX-XX), так и мобильный (XXX-XXX-XX-XX), и не принимающий чего-то другого '(\d{3,3}-){1,2}' - из XXX- или XXX-XXX- '(\d{2,2])-(\d{2,2])' - XX-XX
1 import re
2 regular = "(\d{3,3}-){1,2}(\d{2,2})-(\d{2,2})"
3 pat = re.match(regular, "677-43-22")
4 print pat.group() # 677-43-22
5 pat = re.match(regular, "918-723-42-53")
6 print pat.group() # 918-723-42-53
7 pat = re.match(regular, "918-44")
8 print pat # None
9 pat = re.match(regular, "918754615")
10 print pat # None
4. Пусть также у нас первые три символа номера могут задаваться заглавными буквами из латинского алфавита. '(((\d{3,3})|([A-Z]{3,3}))-)?' - первые XXX, могут быть, могут не быть
1 import re
2 regular = "(((\d{3,3})|([A-Z]{3,3}))-)?\d{3,3}-(\d{2,2})-(\d{2,2})"
3 pat = re.match(regular, "677-43-22")
4 print pat.group() # 677-43-22
5 pat = re.match(regular, "918-723-42-53")
6 print pat.group() # 918-723-42-53
7 pat = re.match(regular, "ABC-723-42-53")
8 print pat.group() # ABC-723-42-53
9 pat = re.match(regular, "918-44")
10 print pat # None
11 pat = re.match(regular, "918754615")
12 print pat # None