Модуль re – регулярные выражения

Цель: Поиск и изменение текста при помощи шаблонов.
Доступно в версиях: 1.5 и более поздних.

Регулярные выражения – это шаблоны для поиска, описываемые при помощи специального синтаксиса. Термин «регулярные выражение»(“regular expressions”) часто сокращается до “regex” или “regexp”. Выражения могут включать в себя строки текста, повторы, составные шаблоны, ветвления и иные сложные правила.

Регулярные выражения чаще всего применяются для обработки текста. К примеру, в шаблонах поиска, в редакторах текста и современных IDE. Они также являются важной частью таких утилит командной строки Unix: sed, grep и awk.

В C, C++ и Python поддержка регулярных выражений реализована при помощи подключаемых библиотек.

Существует пару спецификаций регулярных выражений с открытым исходным кодом. Каждая из них использует базовый синтаксис, но имеет в себя разные расширения дополнительного функционала. Синтаксис, используемый в модуле re, основан на синтаксисе языка программирования Perl с улучшениями, свойственными для Python.

Поиск в тексте по шаблону

Наиболее часто модуль re используется для поиска в тексте по шаблону. Приведенный ниже пример ищет слова ‘this’ и ‘that’ в строке текста.

import repatterns = [ 'this', 'that' ]text = 'Does this text match the pattern?'for pattern in patterns: print 'Looking for "%s" in "%s" ->' %(pattern, text), if re.search(pattern, text): print 'found a match!' else: print 'no match'

Способ search() принимает шаблон, а также текст для поиска, и возвращает объект Match, когда найдено соответствующая строка. Если нет, то search() возвращает значение None.

$ python re_simple.pyLooking for "this" in "Does this text match the pattern?" -> found a match!Looking for "that" in "Does this text match the pattern?" -> no match

Объект Match включает оригинал исходной строки, использованное регулярное выражение и позицию в тексте, где найдено совпадение.

import repattern = 'this'text = 'Does this text match the pattern?'match = re.search(pattern, text)s = match.start()e = match.end()print 'Found "%s" in "%s" from %d to %d("%s")' %(match.re.pattern, match.string, s, e, text[s:e])

Способы start() и end() содержат целочисленные индексы строки, обозначающие, где встречается подходящий под шаблон текст.

$ python re_simple_match.pyFound "this" in "Does this text match the pattern?" from 5 to 9("this")

Компиляция выражений

Модуль re содержит возможности для работы с регулярными выражениями. Но он более эффективен при компиляции выражений, которые использует программа. Функцию compile() преобразует выражение в объект RegexObject.

import re# Предкомпилируем данные шаблоныregexes = [ re.compile(p) for p in [ 'this',                               'that',                                     ]            ]text = 'Does this text match the pattern?'for regex in regexes:    print 'Looking for "%s" in "%s" ->' %(regex.pattern, text),    if regex.search(text):        print 'found a match!'    else:        print 'no match'

Возможности модуля поддерживают кэш. Но его размер ограничен, так что можно избежать лишней нагрузки на сервер при запросе из кэша. Благодаря чему вы перемещаете компиляцию на время старта приложения.

$ python re_simple_compiled.pyLooking for "this" in "Does this text match the pattern?" -> found a match!Looking for "that" in "Does this text match the pattern?" -> no match

Множественные вхождения

До этого момента все шаблоны в приведенных примерах использовали способ search() для поиска единичного вхождения строки. Функцию findall() возвращает все подстроки из текста, которые совпадают с шаблоном.

import retext = 'abbaaabbbbaaaaa'pattern = 'ab'for match in re.findall(pattern, text):    print 'Found "%s"' % match

Подстрока ab встречается дважды.

$ python re_findall.pyFound "ab"Found "ab"

Способ finditer() возвращает итератор, который создает объекты Match вместо строк, возвращаемых способом findall().

import retext = 'abbaaabbbbaaaaa'pattern = 'ab'for match in re.finditer(pattern, text):    s = match.start()    e = match.end()    print 'Found "%s" at %d:%d' %(text[s:e], s, e)

Этот пример находит те же два вхождения подстроки ab, а объект Match показывает их место в оригинальной строке.

$ python re_finditer.pyFound "ab" at 0:2Found "ab" at 5:7

Синтаксис шаблонов

Регулярные выражения поддерживают более мощные шаблоны. Они могут повторяться, т быть привязаны к разным частям исходной строки. А также могут быть в компактной форме, которая не подразумевает перечисление искомого символа внутри шаблона. И все это благодаря метасимволам. Следующие примеры будут использовать тестовую программу, чтобы продемонстрировать вам разнообразие шаблонов.

import redef test_patterns(text, patterns=[]):    """ Given source text and a list of patterns, look for matches for each pattern within the text and print them to stdout.    """    # Показываем позиции символов и введённый текст print print ''.join(str(i/10 or ' ') for i in range(len(text)))    print ''.join(str(i%10) for i in range(len(text)))    print text    # Ищем каждый шаблон в тексте и выводим результаты for pattern in patterns:        print print 'Matching "%s"' % pattern for match in re.finditer(pattern, text):            s = match.start()            e = match.end()            print '  %2d: %2d = "%s"' % (s, e-1, text[s:e])    returnif __name__ == '__main__':    test_patterns('abbaaabbbbaaaaa', ['ab'])

Функцию test_patterns() выводит текст, позиции символов, а также набор строк, соответствующий каждому шаблону.

$ python re_test_patterns.py 11111012345678901234abbaaabbbbaaaaaMatching "ab"   0 :  1 = "ab"   5 :  6 = "ab"

Повторы

Существует пять методов задать повтор символов в шаблоне. Паттерн, за которым следует метасимвол *, повторяется ноль или более раз. При этом ноль значит, что он не обязан присутствовать в тексте.

Замените * на +, и искомый паттерн должен присутствовать в тексте хотя бы раз. Использование ? означает, что шаблон должен встречаться ноль или один раз. Для определённого числа вхождений используйте {m} после шаблона, где m заменяется числом вхождений.

Чтобы задать ограниченное число вхождений, используйте {m,n}, где m – это минимальное число вхождений, а n – максимальное. Если опустить n ({m,}), то это будет означать, что шаблон должен встречаться хотя бы m раз, но без ограничений на максимальное число.

from re_test_patterns import test_patternstest_patterns('abbaaabbbbaaaaa',              [ 'ab*',     # a с последующей ноль или более раз b                'ab+',     # a с последующей одной или более b                'ab?',     # a с последующей ни одной или одной b                'ab{3}',   # a с последующими тремя b                'ab{2,3}', # a с последующими двумя или тремя b                ])

Заметьте, насколько больше вхождений у шаблонов ab* и ab?, чем у ab+.

$ python re_repetition.py 11111012345678901234abbaaabbbbaaaaaMatching "ab*"   0 :  2 = "abb"   3 :  3 = "a"   4 :  4 = "a"   5 :  9 = "abbbb"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "ab+"   0 :  2 = "abb"   5 :  9 = "abbbb"Matching "ab?"   0 :  1 = "ab"   3 :  3 = "a"   4 :  4 = "a"   5 :  6 = "ab"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "ab{3}"   5 :  8 = "abbb"Matching "ab{2,3}"   0 :  2 = "abb"   5 :  8 = "abbb"

Стандартная обработка инструкции повторения заключается в том, чтобы добавить в результат как можно больше символов, пока результат соответствует шаблону. Это так называемое жадное поведение, которое может находить меньшее число отдельных совпадений. Либо совпадения могут включать в себя больше исходного текста, чем требовалось. Такое поведение паттерна можно отключить при помощи символа «?», следующего за инструкцией повторения.

from re_test_patterns import test_patternstest_patterns('abbaaabbbbaaaaa',              [ 'ab*?',     # a с последующей ноль или более раз b                'ab+?',     # a с последующей одной или более b                'ab??',     # a с последующей ни одной или одной b                'ab{3}?',   # a с последующими тремя b                'ab{2,3}?', # a с последующими двумя или тремя b                ])

Отмена жадного потребления исходного текста для шаблона, в котором допускается ноль вхождений b, означает, что подходящая подстрока может вообще не включать в себя b.

$ python re_repetition_non_greedy.py 11111012345678901234abbaaabbbbaaaaaMatching "ab*?"   0 :  0 = "a"   3 :  3 = "a"   4 :  4 = "a"   5 :  5 = "a"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "ab+?"   0 :  1 = "ab"   5 :  6 = "ab"Matching "ab??"   0 :  0 = "a"   3 :  3 = "a"   4 :  4 = "a"   5 :  5 = "a"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "ab{3}?"   5 :  8 = "abbb"Matching "ab{2,3}?"   0 :  2 = "abb"   5 :  7 = "abb"

Наборы символов

Наборы символов – это группы символов, каждый из которых может соответствовать определённому месту в шаблоне. К примеру, шаблону [ab] соответствует a или b.

from re_test_patterns import test_patternstest_patterns('abbaaabbbbaaaaa',              [ '[ab]',    # либо a, либо b                'a[ab]+',  # a с последующими одной или несколькими a или b                'a[ab]+?', # a с последующими одной или несколькими a или b, не жадный                ])

«Жадный» вариант шаблона a[ab]+ вернет всю строку целиком, так как первый символ – это a, а каждый последующий – либо a, либо b.

$ python re_charset.py 11111012345678901234abbaaabbbbaaaaaMatching "[ab]"   0 :  0 = "a"   1 :  1 = "b"   2 :  2 = "b"   3 :  3 = "a"   4 :  4 = "a"   5 :  5 = "a"   6 :  6 = "b"   7 :  7 = "b"   8 :  8 = "b"   9 :  9 = "b"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "a[ab]+"   0 : 14 = "abbaaabbbbaaaaa"Matching "a[ab]+?"   0 :  1 = "ab"   3 :  4 = "aa"   5 :  6 = "ab"  10 : 11 = "aa"  12 : 13 = "aa"

Набор символов также может быть использован для исключения определённых значений. Специальный маркер «^» имеет поиск символов, не входящих в следующий за ним набор.

from re_test_patterns import test_patternstest_patterns('This is some text -- with punctuation.',              [ '[^-. ]+',  # последовательности без -, . или пробелов                ])

Этот шаблон находит все подстроки, которые не содержат символы «-», «.» или пробелы.

$ python re_charset_exclude.py 111111111122222222223333333301234567890123456789012345678901234567This is some text -- with punctuation.Matching "[^-. ]+"   0 :  3 = "This"   5 :  6 = "is"   8 : 11 = "some"  13 : 16 = "text"  21 : 24 = "with"  26 : 36 = "punctuation"

По мере увеличения набора поиск каждого символа становится трудоёмким. Компактный формат шаблона с использованием диапазонов может определить набор символов, включающий в себя все последовательные символы между стартовым и конечным.

from re_test_patterns import test_patternstest_patterns('This is some text -- with punctuation.',              [ '[a-z]+',      # последовательности строчных букв                '[A-Z]+',      # последовательности заглавных букв                '[a-zA-Z]+',   # последовательности строчных или заглавных букв                '[A-Z][a-z]+', # одна заглавная буква, за которой следуют строчные                ])

Диапазон a-z это строчные буквы ASCII, а диапазон A-Z — заглавные буквы ASCII.

$ python re_charset_ranges.py 111111111122222222223333333301234567890123456789012345678901234567This is some text -- with punctuation.Matching "[a-z]+"   1 :  3 = "his"   5 :  6 = "is"   8 : 11 = "some"  13 : 16 = "text"  21 : 24 = "with"  26 : 36 = "punctuation"Matching "[A-Z]+"   0 :  0 = "T"Matching "[a-zA-Z]+"   0 :  3 = "This"   5 :  6 = "is"   8 : 11 = "some"  13 : 16 = "text"  21 : 24 = "with"  26 : 36 = "punctuation"Matching "[A-Z][a-z]+"   0 :  3 = "This"

Особым случаем будет метасимвол точки (.), который означает, что шаблон должен соответствовать любому единичному символу на данной позиции.

from re_test_patterns import test_patternstest_patterns('abbaaabbbbaaaaa',              [ 'a.',    # a с последующим любым символом                'b.',    # b с последующим любым символом                'a.*b',  # a, за которым следует что угодно, заканчивающееся b                'a.*?b', # a, за которым следует что угодно, заканчивающееся b (не жадный)                ])

Сочетание точки с метасимволом повтора может привести к длинным совпадениям, если только не используется нежадная форма поиска.

$ python re_charset_dot.py 11111012345678901234abbaaabbbbaaaaaMatching "a."   0 :  1 = "ab"   3 :  4 = "aa"   5 :  6 = "ab"  10 : 11 = "aa"  12 : 13 = "aa"Matching "b."   1 :  2 = "bb"   6 :  7 = "bb"   8 :  9 = "bb"Matching "a.*b"   0 :  9 = "abbaaabbbb"Matching "a.*?b"   0 :  1 = "ab"   3 :  6 = "aaab"

Escape-коды

Более компактное представление шаблона использует символьные коды для поиска нескольких наборов символов. Escape-коды, использующиеся в модуле re:

Программный код Значение
d Цифра.
D Не цифра.
s Пробел (табуляция, пробел, новая строка и т.п.).
S Не пробел.
w Буква или цифра.
W Не буква и не цифра.

Замечание

Escape-коды начинаются с обратного слеша (). Но в коде Python этот символ должен быть экранирован. Использование литерала r решает эту проблему.

from re_test_patterns import test_patternstest_patterns('This is a prime #1 example!',              [ r'd+', # последовательность цифр r'D+', # последовательность без цифр r's+', # последовательность пробелов r'S+', # последовательность без пробелов r'w+', # последовательность букв или цифр r'W+', # последовательность без букв и цифр                ])

Данные шаблоны используют символьные коды с повторами, чтобы найти последовательности символов в исходной строке.

$ python re_escape_codes.py 11111111112222222012345678901234567890123456This is a prime #1 example!Matching "d+"  17 : 17 = "1"Matching "D+"   0 : 16 = "This is a prime #"  18 : 26 = " example!"Matching "s+"   4 :  4 = " "   7 :  7 = " "   9 :  9 = " "  15 : 15 = " "  18 : 18 = " "Matching "S+"   0 :  3 = "This"   5 :  6 = "is"   8 :  8 = "a"  10 : 14 = "prime"  16 : 17 = "#1"  19 : 26 = "example!"Matching "w+"   0 :  3 = "This"   5 :  6 = "is"   8 :  8 = "a"  10 : 14 = "prime"  17 : 17 = "1"  19 : 25 = "example"Matching "W+"   4 :  4 = " "   7 :  7 = " "   9 :  9 = " "  15 : 16 = " #"  18 : 18 = " "  26 : 26 = "!"

Чтобы создать шаблон для поиска символов, являющих частью синтаксиса регулярных выражений, используйте экранирование.

from re_test_patterns import test_patternstest_patterns(r'd+ D+ s+ S+ w+ W+',              [ r'd+',                r'D+',                r's+',                r'S+',                r'w+',                r'W+',                ])

Данные шаблоны экранируют символы обратного слеша и плюса, так как оба имеют особое назначение в регулярном выражении.

$ python re_escape_escapes.py 111111111122201234567890123456789012d+ D+ s+ S+ w+ W+Matching "d+"   0 :  2 = "d+"Matching "D+"   4 :  6 = "D+"Matching "s+"   8 : 10 = "s+"Matching "S+"  12 : 14 = "S+"Matching "w+"  16 : 18 = "w+"Matching "W+"  20 : 22 = "W+"

Привязка

Также можно легко указать относительное местоположение в исходной строке, в котором шаблон должен появляться.

Код Значение
^ начало текста или строки
$ конец текста или строки
A начало текста
Z конец текста
b пустой символ в начале или конце слова
B пустой символ не с начала и не с конца слова
from re_test_patterns import test_patternstest_patterns('This is some text -- with punctuation.',        [ r'^w+',     # слово в начале строки r'Aw+',    # слово в начале текста r'w+S*$',  # слово в конце строки с необязательным знаком препинания r'w+S*Z', # слово в конце текста с необязательным знаком препинания r'w*tw*',  # слово, содержащее 't'                r'btw+',   # 't' в начале слова r'w+tb',   # 't' в конце слова r'BtB',    # 't' не в начале и не в конце слова                ])

В этом примере шаблоны для поиска слов в начале и в конце строки отличаются. За словом в конце строки следует знак пунктуации, заканчивающий предложение. Шаблон w+$ не подойдёт, так как точка не обозначает букву или цифру.

$ python re_anchoring.py 111111111122222222223333333301234567890123456789012345678901234567This is some text -- with punctuation.Matching "^w+"   0 :  3 = "This"Matching "Aw+"   0 :  3 = "This"Matching "w+S*$"  26 : 37 = "punctuation."Matching "w+S*Z"  26 : 37 = "punctuation."Matching "w*tw*"  13 : 16 = "text"  21 : 24 = "with"  26 : 36 = "punctuation"Matching "btw+"  13 : 16 = "text"Matching "w+tb"  13 : 16 = "text"Matching "BtB"  23 : 23 = "t"  30 : 30 = "t"  33 : 33 = "t"

Ограничение поиска

Если известно, что поиск будет осуществляться только в части исходной строки, можно ещё больше упростить шаблон, указав модулю re ограничить диапазон поиска. К примеру, если искомая строка должна находиться в начале, тогда использование способа match() вместо search() привяжет поиск к началу текста без применения в выражении специального символа.

import retext = 'This is some text -- with punctuation.'pattern = 'is'print 'Text   :', textprint 'Pattern:', patternm = re.match(pattern, text)print 'Match  :', ms = re.search(pattern, text)print 'Search :', s

Строка is расположена не в начале текста, так что она не находится при помощи способа match(). Но эта последовательность появляется в тексте два раза, так что способ search() находит её.

$ python re_match.pyText   : This is some text -- with punctuation.Pattern: isMatch  : NoneSearch : <_sre.SRE_Match object at 0x100452988>

Способ search()принимает необязательные настройки начальной и конечной позиций, чтобы ограничить поиск до части исходной строки.

import retext = 'This is some text -- with punctuation.'pattern = re.compile(r'bw*isw*b')print 'Text:', textprintpos = 0while True:    match = pattern.search(text, pos)    if not match:        break s = match.start()    e = match.end()    print '  %2d : %2d = "%s"' %         (s, e-1, text[s:e])    # Двигаемся далее по тексту для следующего поиска pos = e    

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

$ python re_search_substring.pyText: This is some text -- with punctuation.   0 :  3 = "This"   5 :  6 = "is"

Группировка в шаблонах

Добавление групп в шаблон может изолировать части совпадающего текста, расширяя функции для разбора строк. Группы определяются заключением шаблонов в круглые скобки.

from re_test_patterns import test_patternstest_patterns('abbaaabbb p576ey 68a7dv baaaaa',[ 'a(ab)',    # 'a', за которым следует 'ab''a(a*b*)',  # 'a', за которым следует 0 или пару 'a' и ноль или пару 'b'                'a(ab)*',   # 'a', за которым следует 0 или пару 'ab'                'a(ab)+',   # 'a', за которым следует 1 или пару 'ab'                ])

Любое регулярное выражение может быть включено в более крупное выражение. Все модификаторы повторов могут быть применены к группе как к единому целому, требуя повторения группы в шаблоне.

$ python re_groups.py 11111012345678901234abbaaabbbbaaaaaMatching "a(ab)"   4 :  6 = "aab"Matching "a(a*b*)"   0 :  2 = "abb"   3 :  9 = "aaabbbb"  10 : 14 = "aaaaa"Matching "a(ab)*"   0 :  0 = "a"   3 :  3 = "a"   4 :  6 = "aab"  10 : 10 = "a"  11 : 11 = "a"  12 : 12 = "a"  13 : 13 = "a"  14 : 14 = "a"Matching "a(ab)+"   4 :  6 = "aab"

Чтобы найти части строки, совпадающие с каждой отдельной группой в шаблоне, используйте способ groups() объекта Match.

import retext = 'This is some text -- with punctuation.'print textprintfor pattern in [ r'^(w+)',           # слово в начале строки string r'(w+)S*$',        # слово в конце строки с необязательной пунктуацией r'(btw+)W+(w+)', # слово, начинающееся с 't', далее иное слово r'(w+t)b',         # слово, заканчивающиеся на 't'                 ]:    regex = re.compile(pattern)    match = regex.search(text)    print 'Matching "%s"' % pattern print '  ', match.groups()    print

Match.groups() возвращает последовательность строк в порядке чередования групп в шаблоне.

$ python re_groups_match.pyThis is some text -- with punctuation.Matching "^(w+)"   ('This',)Matching "(w+)S*$"   ('punctuation',)Matching "(btw+)W+(w+)"   ('text', 'with')Matching "(w+t)b"   ('text',)

Если вам не нужны все части, которые соответствуют группам, можно искать совпадение только по одной группе с помощью способа group().

import retext = 'This is some text -- with punctuation.'print 'Input text            :', text# слово, начинающееся с 't', далее иное словоregex = re.compile(r'(btw+)W+(w+)')print 'Pattern               :', regex.patternmatch = regex.search(text)print 'Entire match          :', match.group(0)print 'Word starting with "t":', match.group(1)print 'Word after "t" word   :', match.group(2)

Группа 0 представляет собой строку, совпадающую со всем регулярным выражением. Подгруппы пронумерованы, начиная с 1 в порядке чередования в выражении.

$ python re_groups_individual.pyInput text            : This is some text -- with punctuation.Pattern               : (btw+)W+(w+)Entire match          : text -- withWord starting with "t": textWord after "t" word   : with

Python расширяет базовый синтаксис группировки, добавляя именованные группы. Использование имён для обращения к группам упрощает редактирование шаблона в будущем. Чтобы задать группе имя, используйте синтаксис (P?<name>pattern).

import retext = 'This is some text -- with punctuation.'print textprintfor pattern in [ r'^(?P<first_word>w+)',                 r'(?P<last_word>w+)S*$',                 r'(?P<t_word>btw+)W+(?P<other_word>w+)',                 r'(?P<ends_with_t>w+t)b',                 ]:    regex = re.compile(pattern)    match = regex.search(text)    print 'Matching "%s"' % pattern print '  ', match.groups()    print '  ', match.groupdict()    print

Используйте способ groupdict(), чтобы приобрести словарь, сопоставляющий имена групп с совпадающими подстроками. Именованные шаблоны также включаются в упорядоченную последовательность способа groups().

$ python re_groups_named.pyThis is some text -- with punctuation.Matching "^(?P<first_word>w+)"   ('This',)   {'first_word': 'This'}Matching "(?P<last_word>w+)S*$"   ('punctuation',)   {'last_word': 'punctuation'}Matching "(?P<t_word>btw+)W+(?P<other_word>w+)"   ('text', 'with')   {'other_word': 'with', 't_word': 'text'}Matching "(?P<ends_with_t>w+t)b"   ('text',)   {'ends_with_t': 'text'}

Обновлённая версия возможности test_patterns(), показывающая нумерованные и именованные группы совпадений по шаблону, облегчит понимание следующих примеров.

import redef test_patterns(text, patterns=[]):    """Имея исходный текст и список шаблонов, ищем совпадения для каждого шаблона в тексте и выводим их в stdout.    """    # Показываем позиции символов и введённый текст print print ''.join(str(i/10 or ' ') for i in range(len(text)))    print ''.join(str(i%10) for i in range(len(text)))    print text    # Ищем каждый шаблон в тексте и выводим результаты for pattern in patterns:        print print 'Matching "%s"' % pattern for match in re.finditer(pattern, text):            s = match.start()            e = match.end()            print '  %2d : %2d = "%s"' %                 (s, e-1, text[s:e])            print '    Groups:', match.groups()            if match.groupdict():                print '    Named groups:', match.groupdict()            print return

Группы могут наследовать иные группы для создания более сложных выражений.

from re_test_patterns_groups import test_patternstest_patterns('abbaaabbbbaaaaa',              [r'a((a*)(b*))', # 'a' followed by 0-n 'a' and 0-n 'b'               ])

В данном случае группе (a*) соответствует пустая строка, так что способ groups() вернет пустую строку как подходящее значение.

$ python re_groups_nested.py 11111012345678901234abbaaabbbbaaaaaMatching "a((a*)(b*))"   0 :  2 = "abb"    Groups: ('bb', '', 'bb')   3 :  9 = "aaabbbb"    Groups: ('aabbbb', 'aa', 'bbbb')  10 : 14 = "aaaaa"    Groups: ('aaaa', 'aaaa', '')

Группы также полезны для создания альтернативных шаблонов. Используйте символ «|» для поиска строки, которая должна соответствовать одному или нескольким шаблонам.

Первое выражение в примере совпадает с последовательностью символов a, за которой следует строка из одной буквы a или b. Второй шаблон совпадает с последовательностью символов a, за которой следует строка, которая может включать в себя как a, так и b. Шаблоны похожи, но результат их работы разный.

from re_test_patterns_groups import test_patternstest_patterns('abbaaabbbbaaaaa', [r'a((a+)|(b+))', # 'a', за которым следует последовательность из 'a' или последовательность из 'b'   r'a((a|b)+)',    # 'a', за которым следует последовательность из 'a' или 'b'               ])

Когда в строке нет совпадений с альтернативной группой, но весь шаблон имеет в себя совпадения, то результат работы способа groups() равен None в том месте, где должна располагаться альтернативная группа.

$ python re_groups_alternative.py 11111012345678901234abbaaabbbbaaaaaMatching "a((a+)|(b+))"   0 :  2 = "abb"    Groups: ('bb', None, 'bb')   3 :  5 = "aaa"    Groups: ('aa', 'aa', None)  10 : 14 = "aaaaa"    Groups: ('aaaa', 'aaaa', None)Matching "a((a|b)+)"   0 : 14 = "abbaaabbbbaaaaa"    Groups: ('bbaaabbbbaaaaa', 'a')

Определение группы с подшаблоном полезно, когда строка, совпадающая с подшаблоном, не будет частью искомого. Такие группы называются незахватными. Чтобы создать незахватную группу, используйте синтаксис (?:pattern).

from re_test_patterns_groups import test_patternstest_patterns('abbaaabbbbaaaaa',              [r'a((a+)|(b+))',     # захватываем совпадения r'a((?:a+)|(?:b+))', # не захватываем совпадения               ])

Сравните группы, возвращаемые для захватных и незахватных групп шаблона, которым соответствуют одинаковые результаты.

$ python re_groups_non_capturing.py 11111012345678901234abbaaabbbbaaaaaMatching "a((a+)|(b+))"   0 :  2 = "abb"    Groups: ('bb', None, 'bb')   3 :  5 = "aaa"    Groups: ('aa', 'aa', None)  10 : 14 = "aaaaa"    Groups: ('aaaa', 'aaaa', None)Matching "a((?:a+)|(?:b+))"   0 :  2 = "abb"    Groups: ('bb',)   3 :  5 = "aaa"    Groups: ('aa',)  10 : 14 = "aaaaa"    Groups: ('aaaa',)

Опции поиска

Для изменения обработки регулярных выражений используйте флаги. Они могут объединяться, используя битовую операцию «или», и передаваться в compile(), search(), match()и иные возможности, которые принимают шаблон для поиска.

Регистронезависимый поиск

Использование флага IGNORECASE приводит к тому, чтобы буквенные символы и буквенные диапазоны в шаблоне будут совпадать как для строчных, так и для заглавных букв.

import retext = 'This is some text -- with punctuation.'pattern = r'bTw+'with_case = re.compile(pattern)without_case = re.compile(pattern, re.IGNORECASE)print 'Text            :', textprint 'Pattern         :', patternprint 'Case-sensitive  :', with_case.findall(text)print 'Case-insensitive:', without_case.findall(text)

Так как выражение содержит букву «T», то без использования IGNORECASE единственным подходящим словом будет «This». А когда регистр игнорируется, слово «text» также подходит.

$ python re_flags_ignorecase.pyText            : This is some text -- with punctuation.Pattern         : bTw+Case-sensitive  : ['This']Case-insensitive: ['This', 'text']

Исходный текст из нескольких строк

Есть два флага, которые влияют на поиск в многострочном тексте. Флаг MULTILINE, управляет тем, как шаблон обрабатывает инструкции привязки текста, содержащего символы новой строки. Когда многострочный режим включён, правила привязки для ^ и $ применяются к началу и концу каждой строки в дополнение к привязке ко всему тексту.

import retext = 'This is some text -- with punctuation.nAnd a second line.'pattern = r'(^w+)|(w+S*$)'single_line = re.compile(pattern)multiline = re.compile(pattern, re.MULTILINE)print 'Text        :', repr(text)print 'Pattern     :', patternprint 'Single Line :', single_line.findall(text)print 'Multline    :', multiline.findall(text)

Шаблону в этом примере соответствуют первое или последнее слово исходной строки. Ему соответствует line. в конце текста даже при том, что там нет символа новой строки.

$ python re_flags_multiline.pyText        : 'This is some text -- with punctuation.nAnd a second line.'Pattern     : (^w+)|(w+S*$)Single Line : [('This', ''), ('', 'line.')]Multline    : [('This', ''), ('', 'punctuation.'), ('And', ''), ('', 'line.')]

DOTALL – ещё один флаг для параметра поиска в многострочном тексте. Символу точки в шаблоне соответствует всё, кроме символа новой строки. Данный флаг может символу новой строки также соответствовать метасимволу «.».

import retext = 'This is some text -- with punctuation.nAnd a second line.'pattern = r'.+'no_newlines = re.compile(pattern)dotall = re.compile(pattern, re.DOTALL)print 'Text        :', repr(text)print 'Pattern     :', patternprint 'No newlines :', no_newlines.findall(text)print 'Dotall      :', dotall.findall(text)

Без этого флага каждая строка исходного текста совпадает с шаблоном по отдельности. Добавление флага приводит к тому, что под шаблон подходит весь текст.

$ python re_flags_dotall.pyText        : 'This is some text -- with punctuation.nAnd a second line.'Pattern     : .+No newlines : ['This is some text -- with punctuation.', 'And a second line.']Dotall      : ['This is some text -- with punctuation.nAnd a second line.']

Unicode

В Python 2 объекты str используют набор символов ASCII. Так что шаблон и исходный текст должны быть представлены в кодировке ASCII. Escape-коды, описанные выше, также по умолчанию определяются в терминах ASCII.

Вследствие этого шаблону w+ будет соответствовать слово “French”, но не слово “Français”, так как ç не будет частью набора символов ASCII. Чтобы включить поиск по символам Unicode в Python 2, добавьте флаг UNICODE при компиляции шаблона.

import reimport codecsimport sys# Устанавливаем стандартную кодировку вывода в UTF-8.sys.stdout = codecs.getwriter('UTF-8')(sys.stdout)text = u'Français złoty Österreich'pattern = ur'w+'ascii_pattern = re.compile(pattern)unicode_pattern = re.compile(pattern, re.UNICODE)print 'Text    :', textprint 'Pattern :', patternprint 'ASCII   :', u', '.join(ascii_pattern.findall(text))print 'Unicode :', u', '.join(unicode_pattern.findall(text))

Иные Escape-последовательности (W, b, B, d, D, s и S) также иначе обрабатываются для текста в кодировке Unicode. Так что обработчик регулярных выражений использует базу Unicode для поиска свойств каждого символа.

$ python re_flags_unicode.pyText    : Français złoty ÖsterreichPattern : w+ASCII   : Fran, ais, z, oty, sterreichUnicode : Français, złoty, Österreich

Замечание

Python 3 по умолчанию использует Unicode для всех строк, так что данный флаг не требуется.

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

Компактная форма синтаксиса регулярного выражения может стать помехой по мере увеличения групп в шаблоне. Лучшим вариантом будет использование выражений в многословном режиме, который может добавлять комментарии и дополнительные пробелы.

Шаблон проверки email — адреса проиллюстрирует использование многословного режима. Первая часть распознаёт адрес электронной почты, который заканчивается на .com, .org или .edu.

import readdress = re.compile('[wd.+-]+@([wd.]+.)+(com|org|edu)', re.UNICODE)candidates = [    u'first.last@example.com',    u'first.last+category@gmail.com',    u'valid-address@mail.example.com',    u'not-valid@example.foo',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Matches'    else:        print '  No match'    

Это регулярное выражение сложное. В нём пару классов символов, групп и повторов.

$ python re_email_compact.pyCandidate: first.last@example.com MatchesCandidate: first.last+category@gmail.com MatchesCandidate: valid-address@mail.example.com MatchesCandidate: not-valid@example.foo No match

Преобразование шаблона в многословный формат облегчит его расширение.

import readdress = re.compile(    '''    [wd.+-]+       # имя посетителя    @    ([wd.]+.)+    # домен второго уровня    (com|org|edu)    # необходимо поддерживать больше доменов верхнего уровня    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'first.last@example.com',    u'first.last+category@gmail.com',    u'valid-address@mail.example.com',    u'not-valid@example.foo',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Matches'    else:        print '  No match'

Данному регулярному выражению соответствуют те же строки, но в расширенном формате его легче читать. Комментарии также помогают различать части шаблона.

$ python re_email_verbose.pyCandidate: first.last@example.com MatchesCandidate: first.last+category@gmail.com MatchesCandidate: valid-address@mail.example.com MatchesCandidate: not-valid@example.foo No match

Данная версия шаблона разбивает строки, включающие в себя имя посетителя и email- адрес так, как они могут выводиться в заголовке письма. Сначала следует имя, и оно отделено от email-адреса в угловых скобках (< и >).

import readdress = re.compile(    '''    # Имя состоит из букв и может включать "." в сокращениях     # титула или инициалах.    ((?P<name>       ([w.,]+s+)*[w.,]+)       s*       # Адреса email заключены в угловые скобки: < >       # но нам нужен только один адрес, если мы нашли имя, так что       # оставляем начальную < в данной группе.       <    )? # вся эта группа необязательна    # Сам адрес: username@domain.tld    (?P<email>      [wd.+-]+       # имя посетителя      @      ([wd.]+.)+    # домен второго уровня      (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    )    >? # необязательная закрывающая скобка    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'first.last@example.com',    u'first.last+category@gmail.com',    u'valid-address@mail.example.com',    u'not-valid@example.foo',    u'First Last <first.last@example.com>',    u'No Brackets first.last@example.com',    u'First Last',    u'First Middle Last <first.last@example.com>',    u'First M. Last <first.last@example.com>',    u'<first.last@example.com>',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match name :', match.groupdict()['name']        print '  Match email:', match.groupdict()['email']    else:        print '  No match'

Функция вставлять комментарии в многословное регулярное выражение помогает в поддержке кода. Финальная версия шаблона включает заметки по его реализации для разработчиков. А также пробелы для отделения групп друг от друга и выделения их иерархии.

$ python re_email_with_name.pyCandidate: first.last@example.com Match name : None Match email: first.last@example.comCandidate: first.last+category@gmail.com Match name : None Match email: first.last+category@gmail.comCandidate: valid-address@mail.example.com Match name : None Match email: valid-address@mail.example.comCandidate: not-valid@example.foo No matchCandidate: First Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: No Brackets first.last@example.com Match name : None Match email: first.last@example.comCandidate: First Last No matchCandidate: First Middle Last <first.last@example.com>  Match name : First Middle Last Match email: first.last@example.comCandidate: First M. Last <first.last@example.com>  Match name : First M. Last Match email: first.last@example.comCandidate: <first.last@example.com>  Match name : None Match email: first.last@example.com

Включение флагов в шаблоны

Также можно добавлять флаги в само регулярное выражение. Например, чтобы включить регистронезависимый поиск, добавьте (?i)в начало шаблона.

import retext = 'This is some text -- with punctuation.'pattern = r'(?i)bTw+'regex = re.compile(pattern)print 'Text      :', textprint 'Pattern   :', patternprint 'Matches   :', regex.findall(text)

Так как настройки контролируют метод обработки всего регулярного выражения, они должны указываться в начале шаблона.

$ python re_flags_embedded.pyText      : This is some text -- with punctuation.Pattern   : (?i)bTw+Matches   : ['This', 'text']

Аббревиатуры всех флагов приведены ниже:

Флаг Аббревиатура
IGNORECASE I
MULTILINE M
DOTALL S
UNICODE U
VERBOSE X

Включённые в регулярное выражение флаги можно без проблем объединять путём размещения в одной группе. К примеру, (?imu) имеет регистронезависимый поиск по многострочному тексту в кодировке Unicode.

Поиск вперёд или назад

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

Приведенная ниже версия регулярного выражения использует утверждение положительного поиска вперёд для поиска пары. Синтаксис поиска вперёд следующий:

(?=pattern).import readdress = re.compile(    '''    # Имя состоит из букв и может включать "." в сокращениях     # титула или инициалах.    ((?P<name>       ([w.,]+s+)*[w.,]+     )     s+    ) # имя больше не будет необязательным    # ПОИСК ВПЕРЁД    # Адреса email заключены в угловые скобки, но мы хотим искать    # только парные скобки, либо никаких.    (?= (<.*>$)       # оставшаяся часть заключена в угловые скобки        |        ([^<].*[^>]$) # оставшаяся часть НЕ заключена в угловые скобки      )    <? # необязательная открывающая угловая скобка    # Сам адрес: username@domain.tld    (?P<email>      [wd.+-]+       # имя посетителя      @      ([wd.]+.)+    # домен второго уровня      (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    )    >? # необязательная закрывающая угловая скобка    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'First Last <first.last@example.com>',    u'No Brackets first.last@example.com',    u'Open Bracket <first.last@example.com',    u'Close Bracket first.last@example.com>',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match name :', match.groupdict()['name']        print '  Match email:', match.groupdict()['email']    else:        print '  No match'

В данной версии регулярного выражения есть пару важных изменений. Сейчас имя получателя не будет обязательным. Это означает, что одни лишь адреса не пройдут проверку. Что также уберегает от пар имя/адрес, имеющих некорректный формат.

Правило положительного поиска вперёд означает, что оставшаяся часть строки заключена в несколько угловых скобок или не должна быть непарной скобки. То есть либо присутствуют обе скобки, либо ни одной.

Соответствие для группы поиска вперёд не захватывает никакой части текста, так что остаток шаблона продолжает искать соответствие с того же места, где найдено соответствие.

$ python re_look_ahead.pyCandidate: First Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: No Brackets first.last@example.com Match name : No Brackets Match email: first.last@example.comCandidate: Open Bracket <first.last@example.com No matchCandidate: Close Bracket first.last@example.com>  No match

Утверждение негативного поиска вперёд ((?!pattern)) говорит, что текст с этого места не должен соответствовать шаблону. К примеру, шаблон распознавания email-адреса может быть изменён, чтобы игнорировать адреса noreply, которые часто используются автоматическими системами.

import readdress = re.compile(    '''    ^    # Email адрес: username@domain.tld    # Игнорируем адреса noreply    (?!noreply@.*$)    [wd.+-]+       # имя посетителя    @    ([wd.]+.)+    # домен второго уровня    (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    $    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'first.last@example.com',    u'noreply@example.com',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match:', candidate[match.start():match.end()]    else:        print '  No match'

Адреса, начинающиеся с noreply, не подходят под шаблон, так как утверждение поиска вперёд не работает.

$ python re_negative_look_ahead.pyCandidate: first.last@example.com Match: first.last@example.comCandidate: noreply@example.com No match

Шаблон также может быть написан с использованием утверждения негативного поиска назад после нахождения совпадения для имени посетителя. Синтаксис: ?<!pattern).

import readdress = re.compile(    '''    ^    # Email адрес: username@domain.tld    [wd.+-]+       # имя посетителя    # Игнорируем адреса noreply    (?<!noreply)    @    ([wd.]+.)+    # домен второго уровня    (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    $    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'first.last@example.com',    u'noreply@example.com',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match:', candidate[match.start():match.end()]    else:        print '  No match'

Поиск назад работает пару иначе, чем поиск вперёд. В приведенном ниже регулярном выражении должен использоваться шаблон фиксированной длины. Повторы допустимы, если только их фиксированное число.

$ python re_negative_look_behind.pyCandidate: first.last@example.com Match: first.last@example.comCandidate: noreply@example.com No match

Утверждение положительного поиска вперёд может быть использовано для поиска текста, следующего за шаблоном. Синтаксис: (?<=pattern). Приведенное ниже регулярное выражение находит упоминания посетителей Twitter.

import retwitter = re.compile(    '''    # Имя посетителя twitter: @username    (?<=@)    ([wd_]+)       # Имя посетителя    ''',    re.UNICODE | re.VERBOSE)text = '''This text includes two Twitter handles.One for @ThePSF, and one for the author, @doughellmann.'''print textfor match in twitter.findall(text):    print 'Handle:', match

Данному шаблону соответствуют последовательности символов, которые могут представлять собой имя посетителя Twitter, если они начинаются с @.

$ python re_look_behind.pyThis text includes two Twitter handles.One for @ThePSF, and one for the author, @doughellmann.Handle: ThePSFHandle: doughellmann

Выражения, ссылающиеся на себя

Совпадения могут быть использованы в последующих частях регулярного выражения. Пример с email-адресами может быть дополнен, чтобы соответствовать только адресам, состоящим из имени и фамилии человека. Для этого используется обратная ссылка на данные группы. Самый простой метод достичь этого – обратиться к одной из предыдущих групп по номеру, используя num.

import readdress = re.compile(    r'''    # Обычное имя    (w+)               # имя s+    (([w.]+)s+)?      # необязательное отчество или инициалы     (w+)               # фамилия s+    <    # Email адрес: first_name.last_name@domain.tld    (?P<email>      1               # имя      .      4               # фамилия      @      ([wd.]+.)+    # домен второго уровня      (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    )    >    ''',    re.UNICODE | re.VERBOSE | re.IGNORECASE)candidates = [    u'First Last <first.last@example.com>',    u'Different Name <first.last@example.com>',    u'First Middle Last <first.last@example.com>',    u'First M. Last <first.last@example.com>',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match name :', match.group(1), match.group(4)        print '  Match email:', match.group(5)    else:        print '  No match'

Однако создание обратных ссылок по порядковым номерам имеет в себя пару недостатков. При изменении регулярного выражения необходимо повторно пересчитывать группы и, возможно, изменять каждую ссылку.

Таким образом могут быть использованы только 99 ссылок. Трехзначный номер будет интерпретирован как восьмеричное представление символа, а не ссылка на группу.

$ python re_refer_to_group.pyCandidate: First Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: Different Name <first.last@example.com>  No matchCandidate: First Middle Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: First M. Last <first.last@example.com>  Match name : First Last Match email: first.last@example.com

Обработчик регулярных выражений в Python использует синтаксис (?P=name), чтобы обратиться к значению именованной группы, для которой ранее было найдено совпадение.

import readdress = re.compile(    '''    # Обычное имя    (?P<first_name>w+)    s+    (([w.]+)s+)?      # необязательное отчество или инициалы    (?P<last_name>w+)    s+    <    # Email адрес: first_name.last_name@domain.tld    (?P<email>      (?P=first_name)      .      (?P=last_name)      @      ([wd.]+.)+    # домен второго уровня      (com|org|edu)    # ограничиваем допустимые домены верхнего уровня    )    >    ''',    re.UNICODE | re.VERBOSE | re.IGNORECASE)candidates = [    u'First Last <first.last@example.com>',    u'Different Name <first.last@example.com>',    u'First Middle Last <first.last@example.com>',    u'First M. Last <first.last@example.com>',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match name :', match.groupdict()['first_name'], match.groupdict()['last_name']        print '  Match email:', match.groupdict()['email']    else:        print '  No match'

Регулярное выражение email-адреса компилируется с флагом IGNORECASE, так как имена начинаются с заглавной буквы, а email- адреса нет.

$ python re_refer_to_named_group.pyCandidate: First Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: Different Name <first.last@example.com>  No matchCandidate: First Middle Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: First M. Last <first.last@example.com>  Match name : First Last Match email: first.last@example.com

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

Синтаксис проверки группы на соответствие: (?(id)yes-expression|no-expression), где id – имя или номер группы, yes-expression – шаблон, используемый, если группа нашла соответствие, no-expression – шаблон, используемый, если соответствие не найдено.

import readdress = re.compile(    '''    ^    # Имя состоит из букв и может включать "." в сокращениях     # титула или инициалах.    (?P<name>       ([w.]+s+)*[w.]+     )?    s*    # Адреса email заключены в угловые скобки, но мы хотим искать    # скобки, если нашли имя.    (?(name)      # остаток заключается в угловые скобки, так как мы нашли имя      (?P<brackets>(?=(<.*>$)))      |      # остаток не заключается в угловые скобки, если нет имени      (?=([^<].*[^>]$))     )    # Ищем угловую скобку, только если наш поиск вперёд     # нашёл обе скобки.    (?(brackets)<|s*)    # Сам адрес email: username@domain.tld    (?P<email>      [wd.+-]+       # имя посетителя      @      ([wd.]+.)+    # домен второго уровня      (com|org|edu)    # ограничиваем допустимые домены верхнего уровня     )    # Ищем угловую скобку, только если наш поиск вперёд     # нашёл обе скобки.    (?(brackets)>|s*)    $    ''',    re.UNICODE | re.VERBOSE)candidates = [    u'First Last <first.last@example.com>',    u'No Brackets first.last@example.com',    u'Open Bracket <first.last@example.com',    u'Close Bracket first.last@example.com>',    u'no.brackets@example.com',    ]for candidate in candidates:    print print 'Candidate:', candidate match = address.search(candidate)    if match:        print '  Match name :', match.groupdict()['name']        print '  Match email:', match.groupdict()['email']    else:        print '  No match'

Эта версия обработчика email- адреса использует два теста. Если группа name находит соответствие, тогда начало поиска вперёд требует наличия обеих угловых скобок и использует группу brackets.

Если группа name не находит совпадений, требуется, чтобы остаток текста не содержал угловых скобок вокруг него. Если задана группа brackets, программный код шаблона имеет скобки в исходной строке, используя буквенный шаблон, иначе в соответствие попал бы любой пробел.

$ python re_id.pyCandidate: First Last <first.last@example.com>  Match name : First Last Match email: first.last@example.comCandidate: No Brackets first.last@example.com No matchCandidate: Open Bracket <first.last@example.com No matchCandidate: Close Bracket first.last@example.com>  No matchCandidate: no.brackets@example.com Match name : None Match email: no.brackets@example.com

Изменение строк при помощи шаблонов

Модуль re также может изменять текст, используя регулярные выражения в виде механизма поиска. Используйте способ sub(), чтобы заменить все вхождения шаблона другой строкой.

import rebold = re.compile(r'*{2}(.*?)*{2}', re.UNICODE)text = 'Make this **bold**.  This **too**.'print 'Text:', textprint 'Bold:', bold.sub(r'<b>1</b>', text)

Ссылки на текст, соответствующий шаблону, могут быть вставлены при помощи синтаксиса num, использованного для обратных ссылок.

$ python re_sub.pyText: Make this **bold**.  This **too**.Bold: Make this <b>bold</b>.  This <b>too</b>.

Чтобы применять именованные группы для замен, используйте синтаксис g<name>.

import rebold = re.compile(r'*{2}(?P<bold_text>.*?)*{2}', re.UNICODE)text = 'Make this **bold**.  This **too**.'print 'Text:', textprint 'Bold:', bold.sub(r'<b>g<bold_text></b>', text)

Синтаксис g<name> также работает с пронумерованными ссылками, и его использование исключает путаницу между номерами групп и окружающими их цифровыми символами.

$ python re_sub_named_groups.pyText: Make this **bold**.  This **too**.Bold: Make this <b>bold</b>.  This <b>too</b>.

Передача значения счёта может ограничить число производимых замен.

import rebold = re.compile(r'*{2}(.*?)*{2}', re.UNICODE)text = 'Make this **bold**.  This **too**.'print 'Text:', textprint 'Bold:', bold.sub(r'<b>1</b>', text, count=1)

Сделана только одна замена, так как count равен 1.

$ python re_sub_count.pyText: Make this **bold**.  This **too**.Bold: Make this <b>bold</b>.  This **too**.

Способ subn() работает так же, как sub(), за исключением того, что он возвращает как изменённую строку, так и число произведённых замен.

import rebold = re.compile(r'*{2}(.*?)*{2}', re.UNICODE)text = 'Make this **bold**.  This **too**.'print 'Text:', textprint 'Bold:', bold.subn(r'<b>1</b>', text)

Поиск по шаблону нашёл два соответствия.

$ python re_subn.pyText: Make this **bold**.  This **too**.Bold: ('Make this <b>bold</b>.  This <b>too</b>.', 2)

Разбиение строк при помощи шаблонов

str.split() – один из часто используемых способов разбиения строк для их последующего разбора. Но он поддерживает только символьные значения в виде разделителей. А иногда надо регулярное выражение, если исходный текст отформатирован неодинаково. Например, многие языки разметки текста определяют разделители параграфов как два (или более) символа новой строки (n). В этом случае str.split() не может быть использован.

Стратегия определения параграфов при помощи способа findall() использовала бы шаблон вроде (.+?)n{2,}.

import retext = 'Paragraph onenon two lines.nnParagraph two.nnnParagraph three.'for num, para in enumerate(re.findall(r'(.+?)n{2,}', text, flags=re.DOTALL)):    print num, repr(para)    print

Этот шаблон не смог бы найти параграф в конце текста, как показано в примере: “Paragraph three.” не будет частью вывода.

$ python re_paragraphs_findall.py0 'Paragraph onenon two lines.'1 'Paragraph two.'

Расширение регулярного выражения так, чтобы он искал параграф с двумя (или более) символами новой строки, решает проблему, но усложняет шаблон. Использование re.split() вместо re.findall() справляется с проблемой разделения параграфов автоматически и может ограничиться простым шаблоном.

import retext = 'Paragraph onenon two lines.nnParagraph two.nnnParagraph three.'print 'With findall:'for num, para in enumerate(re.findall(r'(.+?)(n{2,}|$)', text, flags=re.DOTALL)):    print num, repr(para)    printprintprint 'With split:'for num, para in enumerate(re.split(r'n{2,}', text)):    print num, repr(para)    print

Аргумент шаблона в способе split() более точно отражает спецификацию разметки: два (или более) символа новой строки обозначают разделение между параграфами исходного текста.

$ python re_split.pyWith findall:0 ('Paragraph onenon two lines.', 'nn')1 ('Paragraph two.', 'nnn')2 ('Paragraph three.', '')With split:0 'Paragraph onenon two lines.'1 'Paragraph two.'2 'Paragraph three.'

Заключение регулярного выражения в скобки, чтобы определить группу, заставляет способ split() работать как str.partition(). После этого он возвращает значения разделителей вместе с остальными частями строки.

import retext = 'Paragraph onenon two lines.nnParagraph two.nnnParagraph three.'printprint 'With split:'for num, para in enumerate(re.split(r'(n{2,})', text)):    print num, repr(para)    print

Сейчас отображение содержит каждый параграф, а также последовательность символов новой строки, которые разделяют параграфы.

$ python re_split_groups.pyWith split:0 'Paragraph onenon two lines.'1 'nn'2 'Paragraph two.'3 'nnn'4 'Paragraph three.'

Смотрите также

re – стандартная документация библиотеки модуля.

Regular Expression HOWTO – введение в регулярные выражения для Python-разработчиков от Эндрю Кучлинга.

Kodos – интерактивный инструмент тестирования регулярных выражений от Фила Шварца.

Википедия: регулярные выражения – введение и основные понятия, концепции и техники работы с регулярными выражениями.

locale – модуль для установки конфигурации при работе с текстом в кодировке Unicode.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *