Kodomo

Пользователь

Учебная страница курса биоинформатики,
год поступления 2017

Указания к заданию 11

Регулярные выражения

http://kodomo.fbb.msu.ru/~pdd/Regular_expressions.png

Часто возникает необходимость проверить, является ли данная строка корректным именем пользователя, последовательности и тд.

Кроме того, вы можете точно не знать, как пишется авторами искомое вами слово. К примеру, для гемоглобина используется как слово hemoglobin, так и слово haemoglobin (и еще пара грамматически не очень правильных вариантов)

Можно, конечно, сделать список со всеми вариантами написания искомого слова.

   1 possible_hem = ["hemoglobin", "haemoglobin", ...]
   2 line = raw_input()
   3 for hem_word in possible_hem:
   4     if hem_word in line:
   5         print "Hem exists"

Во-первых, искать каждое слово в строке по очереди – медленно, а во-вторых, если нужно выделить группу слов и для каждого из них есть разные написания, некоторые слова могут не встречаться в группе и т.д., то такой подход превращается в сущий ад.

Для того, чтобы избежать этой проблемы (и создать новую) и созданы регулярные выражения.

Регулярное выражение — это последовательность символов(не меняют своего значения) и спецсимволов (имеют специальный смысл в пределах регулярного выражения), описывающая какую-то группу слов.

Правила составления регулярного выражения

  1. Большинство букв и символов соответствуют сами себе. Например, регулярное выражение "test" будет в точности соответствовать строке "test".
  2. Исключения:  - . ^ $ * + ? { [ ] \ | ( ) 

  3. Точка соответствует любому символу.
  4. [любые_обычные_символы] будет соответствовать любому из перечисленных в квадратных скобочках символу. Например, [aAb] соответствует a или A или b.

  5. Если нужно указать диапазон символов (цифры от 0 до 5, например), используется выражение вида [0-5]. При этом заглавные и строчные буквы идут отдельно, то есть, например, [A-Z] соответсвует любой заглавной букве, но никакой из строчных.
  6. Шапочка ^ имеет два значения:
    • Отрицание. Валидно только В НАЧАЛЕ пары квадратных скобок. То есть [^0] — любой символ, кроме 0.

    • Начало строки в начале регулярного выражения. "^Петя" будет соответствовать слову "Петя" только в начале строки.
  7. Знак доллара $ означает конец строки: "Петя$" будет соответствовать слову "Петя" только в конце строки.
  8. Обратная косая черта \ используется для экранирования спецсимвола, то есть в случае спецсимволов он превращает их в обычные. При этом некоторые комбинации её с обычными обозначают определенные множества:
    • \d Соответствует любой цифре; эквивалент класса [0-9].
    • \D Соответствует любому нечисловому символу; эквивалент класса [^0-9].
    • \t (внезапно!) обозначает табулятор.
    • \s Соответствует любому символу из класса whitespace ("невидимому"), в том числе пробелу и табулятору.
    • \S Соответствует любому не-whitespace (то есть видимому) символу.
    • \w Соответствует любой букве или цифре, к которым затесалось нижнее подчёркивание; эквивалент [a-zA-Z0-9_].

    • \W Наоборот; эквивалент [^a-zA-Z0-9_].

  9. Круглые скобки выделяют группу символов. В выражении "P(eti)a" выделена отдельная группа "eti"; позже будет понятно, зачем это сделано.
  10. +, располагающийся после любого символа, множества [] или группы символов () означает, что символ/элементы из множества/группа символов должны встречаться не менее чем один раз.
  11. Аналогично, ? означает, что символ или группа может встретиться 0 или 1 раз.
  12. Звезда * означает, что символ или группа может встретиться любое число раз, включая 0. Например, a.*b соответствует любому слову, начинающемуся с a и заканчивающемуся b.
  13. Обобщением всего этого является {min, max}, расположенный также после любого символа, множество [] или группы символов () и значащий, что они должны встречаться не менее min раз и не более max раз. То есть ? эквивалентен {0,1}, + эквивалентен {1,} и * эквивалентна {0,}.
  14. Вертикальная черта | означает "или": "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. Напишем регулярное выражение, соответствующее трем подряд идущем цифрам

   1 import re
   2 regular = "\d\d\d"
   3 pat = re.match(regular, "123")
   4 print pat.group() # "123"
   5 pat = re.match(regular, "123 abcd")
   6 print pat.group() # "123"
   7 pat = re.match(regular, "abcd")
   8 print pat # None

Очевидно, можно написать проще

   1 import re
   2 regular = "\d{3,3}"
   3 pat = re.match(regular, "123")
   4 print pat.group() # "123"
   5 pat = re.match(regular, "123 abcd")
   6 print pat.group() # "123"
   7 pat = re.match(regular, "abcd")
   8 print pat # None

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

http://kodomo.fbb.msu.ru/~pdd/regex-101-4-638.jpg