Рубрики
Без рубрики

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

Автор оригинала: Doug Hellmann.

Цель:

Поиск внутри и изменение текста с использованием формальных шаблонов.

Регулярные выражения являются шаблонами сопоставления текста, описанные с формальным синтаксисом. Шаблоны интерпретируются как набор инструкций, которые затем выполняются со строкой в качестве ввода для создания соответствующего подмножества или модифицированной версии оригинала. Термин «регулярные выражения» часто сокращается до «Regex» или «Regexp» в разговоре. Выражения могут включать в себя буквальное сопоставление текста, повторение, композицию шаблонов, разветвление и другие сложные правила. Большое количество проблем с анализом легче решить с регулярным выражением, чем создание специального целевого лексера и парсера.

Регулярные выражения обычно используются в приложениях, которые включают много текстовой обработки. Например, они обычно используются в качестве шаблонов поиска в программах редактирования текста, используемые разработчиками, включая VI, Emacs и современные IDE. Они также являются неотъемлемой частью утилит командной строки UNIX, такие как SED, GREP и AWK. Многие языки программирования включают поддержку регулярных выражений в языковом синтаксисе (Perl, Ruby, AWK и TCL). Другие языки, такие как C, C ++ и Python, поддерживают регулярные выражения через библиотеки расширения.

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

Примечание

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

Найти шаблонов в тексте

Наиболее распространенное использование для RE - поиск шаблонов в тексте. Функция

Search () определяет шаблон и текст для сканирования и возвращает объект MATCH , когда шаблон найден. Если шаблон не найден, <Код> Поиск () Возврат <код> Нет .

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

re_simple_match.py

import re

pattern  'this'
text  'Does this text match the pattern?'

match  re.search(pattern, text)

s  match.start()
e  match.end()

print('Found "{}"\nin "{}"\nfrom {} to {} ("{}")'.format(
    match.re.pattern, match.string, s, e, text[s:e]))

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

$ python3 re_simple_match.py

Found "this"
in "Does this text match the pattern?"
from 5 to 9 ("this")

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

Хотя RE включает функции модуля на уровне модулей для работы с регулярными выражениями в качестве текстовых строк, более эффективно для компиляции выражений, которые используются программа. Функция Compile () преобразует строку экспрессии в <код> RegexObject .

re_simple_compiled.py

import re

# Precompile the patterns
regexes  [
    re.compile(p)
    for p in ['this', 'that']
]
text  'Does this text match the pattern?'

print('Text: {!r}\n'.format(text))

for regex in regexes:
    print('Seeking "{}" ->'.format(regex.pattern),
          end' ')

    if regex.search(text):
        print('match!')
    else:
        print('no match')

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

$ python3 re_simple_compiled.py

Text: 'Does this text match the pattern?'

Seeking "this" -> match!
Seeking "that" -> no match

Несколько совпадений

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

re_findall.py

import re

text  'abbaaabbbbaaaaa'

pattern  'ab'

for match in re.findall(pattern, text):
    print('Found {!r}'.format(match))

Этот пример входной строки включает в себя два экземпляра AB .

$ python3 re_findall.py

Found 'ab'
Found 'ab'

Функция Finditer () возвращает итератор, который производит <код> match , а не строки, возвращаемые findall () .

re_finditer.py

import re

text  'abbaaabbbbaaaaa'

pattern  'ab'

for match in re.finditer(pattern, text):
    s  match.start()
    e  match.end()
    print('Found {!r} at {:d}:{:d}'.format(
        text[s:e], s, e))

Этот пример находит те же два вхождения Ab , а <код> Match показывает, где они находятся на исходном входе.

$ python3 re_finditer.py

Found 'ab' at 0:2
Found 'ab' at 5:7

Рисунок синтаксиса

Обычные выражения поддерживают более мощные шаблоны, чем простые буквальные текстовые строки. Узоры могут повторяться, могут быть привязаны к разным логическим локациям на входе и могут быть выражены в компактных формах, которые не требуют присутствия каждого буквального характера в шаблоне. Все эти функции используются путем сочетания буквальных текстовых значений с помощью Meta-символов , которые являются частью синтаксиса шаблона регулярного выражения, реализованного Re .

re_test_patterns.py

import re


def 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.
    """
    # Look for each pattern in the text and print the results
    for pattern, desc in patterns:
        print("'{}' ({})\n".format(pattern, desc))
        print("  '{}'".format(text))
        for match in re.finditer(pattern, text):
            s  match.start()
            e  match.end()
            substr  text[s:e]
            n_backslashes  text[:s].count('\\')
            prefix  '.' * (s + n_backslashes)
            print("  {}'{}'".format(prefix, substr))
        print()
    return


if __name__  '__main__':
    test_patterns('abbaaabbbbaaaaa',
                  [('ab', "'a' followed by 'b'"),
                   ])

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

$ python3 re_test_patterns.py

'ab' ('a' followed by 'b')

  'abbaaabbbbaaaaa'
  'ab'
  .....'ab'

Репетиция

Есть пять способов выражения повторения в шаблоне. Узор, за которым следует мета-символ <код> * , повторяется нулю или более раз (позволяя шаблону повторить нулевое время означает, что ему не нужно появляться совсем для соответствия). Если <код> * заменяется <код> + , шаблон должен появиться хотя бы один раз. Использование ? означает, что шаблон появляется ноль или один раз. Для определенного количества вхождений используйте <код> {m} после шаблона, где <код> m – это количество раз, когда шаблон должен повторяться. Наконец, чтобы позволить переменную, но ограниченное количество повторений, используйте <код> {m, n} , где <код> m является минимальным количеством повторений и <код> N это максимум. Оставление <код> n (<код> {m,} ) означает, что значение должно отображаться, по меньшей мере, M Times, без максимума.

re_repetition.py

from re_test_patterns import test_patterns

test_patterns(
    'abbaabbba',
    [('ab*', 'a followed by zero or more b'),
     ('ab+', 'a followed by one or more b'),
     ('ab?', 'a followed by zero or one b'),
     ('ab{3}', 'a followed by three b'),
     ('ab{2,3}', 'a followed by two to three b')],
)

Для AB * и AB? , чем <код> ab + .

$ python3 re_repetition.py

'ab*' (a followed by zero or more b)

  'abbaabbba'
  'abb'
  ...'a'
  ....'abbb'
  ........'a'

'ab+' (a followed by one or more b)

  'abbaabbba'
  'abb'
  ....'abbb'

'ab?' (a followed by zero or one b)

  'abbaabbba'
  'ab'
  ...'a'
  ....'ab'
  ........'a'

'ab{3}' (a followed by three b)

  'abbaabbba'
  ....'abbb'

'ab{2,3}' (a followed by two to three b)

  'abbaabbba'
  'abb'
  ....'abbb'

При обработке инструкции повторения Re обычно потребляют как можно больше ввода, соответствующих шаблону. Это так называемое поведение Greedy может привести к меньшему количеству индивидуальных совпадений, или матчи могут включать в себя больше входного текста, чем предназначен. Жадность может быть выключена, следуя инструкции по повторению с помощью ? .

re_repetition_non_greedy.py

from re_test_patterns import test_patterns

test_patterns(
    'abbaabbba',
    [('ab*?', 'a followed by zero or more b'),
     ('ab+?', 'a followed by one or more b'),
     ('ab??', 'a followed by zero or one b'),
     ('ab{3}?', 'a followed by three b'),
     ('ab{2,3}?', 'a followed by two to three b')],
)

Отключение жадного потребления ввода для любого из шаблонов, где разрешены нулевые вхождения <Код> B , означает, что соответствующая подстрока не включает в себя какие-либо <код> b .

$ python3 re_repetition_non_greedy.py

'ab*?' (a followed by zero or more b)

  'abbaabbba'
  'a'
  ...'a'
  ....'a'
  ........'a'

'ab+?' (a followed by one or more b)

  'abbaabbba'
  'ab'
  ....'ab'

'ab??' (a followed by zero or one b)

  'abbaabbba'
  'a'
  ...'a'
  ....'a'
  ........'a'

'ab{3}?' (a followed by three b)

  'abbaabbba'
  ....'abbb'

'ab{2,3}?' (a followed by two to three b)

  'abbaabbba'
  'abb'
  ....'abb'

Наборы персонажей

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

re_charset.py

from re_test_patterns import test_patterns

test_patterns(
    'abbaabbba',
    [('[ab]', 'either a or b'),
     ('a[ab]+', 'a followed by 1 or more a or b'),
     ('a[ab]+?', 'a followed by 1 or more a or b, not greedy')],
)

Жадная форма выражения (<код> A [AB] + ) потребляет всю строку, поскольку первая буква <код> и каждый последующий символ либо <код> A или <код> b .

$ python3 re_charset.py

'[ab]' (either a or b)

  'abbaabbba'
  'a'
  .'b'
  ..'b'
  ...'a'
  ....'a'
  .....'b'
  ......'b'
  .......'b'
  ........'a'

'a[ab]+' (a followed by 1 or more a or b)

  'abbaabbba'
  'abbaabbba'

'a[ab]+?' (a followed by 1 or more a or b, not greedy)

  'abbaabbba'
  'ab'
  ...'aa'

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

re_charset_exclude.py

from re_test_patterns import test_patterns

test_patterns(
    'This is some text -- with punctuation.',
    [('[^-. ]+', 'sequences without -, ., or space')],
)

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

$ python3 re_charset_exclude.py

'[^-. ]+' (sequences without -, ., or space)

  'This is some text -- with punctuation.'
  'This'
  .....'is'
  ........'some'
  .............'text'
  .....................'with'
  ..........................'punctuation'

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

re_charset_ranges.py

from re_test_patterns import test_patterns

test_patterns(
    'This is some text -- with punctuation.',
    [('[a-z]+', 'sequences of lowercase letters'),
     ('[A-Z]+', 'sequences of uppercase letters'),
     ('[a-zA-Z]+', 'sequences of letters of either case'),
     ('[A-Z][a-z]+', 'one uppercase followed by lowercase')],
)

Здесь диапазон A-Z включает в себя строчные буквы ASCII и диапазон <код> A-Z включает в себя буквы прописных ascii. Диапазоны также могут быть объединены в единый набор символов.

$ python3 re_charset_ranges.py

'[a-z]+' (sequences of lowercase letters)

  'This is some text -- with punctuation.'
  .'his'
  .....'is'
  ........'some'
  .............'text'
  .....................'with'
  ..........................'punctuation'

'[A-Z]+' (sequences of uppercase letters)

  'This is some text -- with punctuation.'
  'T'

'[a-zA-Z]+' (sequences of letters of either case)

  'This is some text -- with punctuation.'
  'This'
  .....'is'
  ........'some'
  .............'text'
  .....................'with'
  ..........................'punctuation'

'[A-Z][a-z]+' (one uppercase followed by lowercase)

  'This is some text -- with punctuation.'
  'This'

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

re_charset_dot.py

from re_test_patterns import test_patterns

test_patterns(
    'abbaabbba',
    [('a.', 'a followed by any one character'),
     ('b.', 'b followed by any one character'),
     ('a.*b', 'a followed by anything, ending in b'),
     ('a.*?b', 'a followed by anything, ending in b')],
)

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

$ python3 re_charset_dot.py

'a.' (a followed by any one character)

  'abbaabbba'
  'ab'
  ...'aa'

'b.' (b followed by any one character)

  'abbaabbba'
  .'bb'
  .....'bb'
  .......'ba'

'a.*b' (a followed by anything, ending in b)

  'abbaabbba'
  'abbaabbb'

'a.*?b' (a followed by anything, ending in b)

  'abbaabbba'
  'ab'
  ...'aab'

Коды побега

Еще более компактное представление использует коды Escape для нескольких предопределенных наборов символов. Коды спасения, распознаваемые Re , перечислены в таблице ниже.

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

Код

Смысл

\ D.

цифра

\ D.

незначительный

\ S.

WhiteSpace (вкладка, пространство, новая линия и т. Д.)

\ S.

небеловой пробел

\ W.

буквенно-цифровой

\ W.

не буквенно-цифровой

Примечание

Ускоты указываются префикс персонажа с обратной ячейкой (<код> \ ). К сожалению, обратная косая черта должна быть сбежена в обычных строках Python, и что приводит к сложным выражениям. Использование строк Roar , созданные путем префикса буквального значения с помощью R , устраняет эту проблему и поддерживает читаемость.

re_escape_codes.py

from re_test_patterns import test_patterns

test_patterns(
    'A prime #1 example!',
    [(r'\d+', 'sequence of digits'),
     (r'\D+', 'sequence of non-digits'),
     (r'\s+', 'sequence of whitespace'),
     (r'\S+', 'sequence of non-whitespace'),
     (r'\w+', 'alphanumeric characters'),
     (r'\W+', 'non-alphanumeric')],
)

Эти образцы выражений объединяют коды Escape с повторением, чтобы найти последовательности таких символов в входной строке.

$ python3 re_escape_codes.py

'\d+' (sequence of digits)

  'A prime #1 example!'
  .........'1'

'\D+' (sequence of non-digits)

  'A prime #1 example!'
  'A prime #'
  ..........' example!'

'\s+' (sequence of whitespace)

  'A prime #1 example!'
  .' '
  .......' '
  ..........' '

'\S+' (sequence of non-whitespace)

  'A prime #1 example!'
  'A'
  ..'prime'
  ........'#1'
  ...........'example!'

'\w+' (alphanumeric characters)

  'A prime #1 example!'
  'A'
  ..'prime'
  .........'1'
  ...........'example'

'\W+' (non-alphanumeric)

  'A prime #1 example!'
  .' '
  .......' #'
  ..........' '
  ..................'!'

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

re_escape_escapes.py

from re_test_patterns import test_patterns

test_patterns(
    r'\d+ \D+ \s+',
    [(r'\\.\+', 'escape code')],
)

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

$ python3 re_escape_escapes.py

'\\.\+' (escape code)

  '\d+ \D+ \s+'
  '\d+'
  .....'\D+'
  ..........'\s+'

Закрепление

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

Коды крепления регулярных выражений

Код

Смысл

Начало строки или линии

конец строки или линии

\ А.

Начало строки

\ Z.

конец строки

\ b.

пустая строка в начале или конце слова

\ B.

пустая строка не в начале или конце слова

re_anchoring.py

from re_test_patterns import test_patterns

test_patterns(
    'This is some text -- with punctuation.',
    [(r'^\w+', 'word at start of string'),
     (r'\A\w+', 'word at start of string'),
     (r'\w+\S*$', 'word near end of string'),
     (r'\w+\S*\Z', 'word near end of string'),
     (r'\w*t\w*', 'word containing t'),
     (r'\bt\w+', 't at start of word'),
     (r'\w+t\b', 't at end of word'),
     (r'\Bt\B', 't, not start or end of word')],
)

Шаблоны в примере для сопоставления слов в начале и конце строки разные, потому что слово в конце строки следует пунктуацию для завершения предложения. Шаблон <код> \ w + $ не будет соответствовать, поскольку <код>. не считается буквенно-цифровым символом.

$ python3 re_anchoring.py

'^\w+' (word at start of string)

  'This is some text -- with punctuation.'
  'This'

'\A\w+' (word at start of string)

  'This is some text -- with punctuation.'
  'This'

'\w+\S*$' (word near end of string)

  'This is some text -- with punctuation.'
  ..........................'punctuation.'

'\w+\S*\Z' (word near end of string)

  'This is some text -- with punctuation.'
  ..........................'punctuation.'

'\w*t\w*' (word containing t)

  'This is some text -- with punctuation.'
  .............'text'
  .....................'with'
  ..........................'punctuation'

'\bt\w+' (t at start of word)

  'This is some text -- with punctuation.'
  .............'text'

'\w+t\b' (t at end of word)

  'This is some text -- with punctuation.'
  .............'text'

'\Bt\B' (t, not start or end of word)

  'This is some text -- with punctuation.'
  .......................'t'
  ..............................'t'
  .................................'t'

Сдерживая поиск

В ситуациях, когда он заранее известен, что следует искать только подмножество полного ввода, регулярное значение выражения может быть дополнительно ограничено, сообщая Re для ограничения диапазона поиска. Например, если шаблон должен появиться в передней части ввода, а затем с использованием Match () вместо <кода> поиск () закрепляет поиск без необходимости явного включения якоря в поиске шаблона.

re_match.py

import re

text  'This is some text -- with punctuation.'
pattern  'is'

print('Text   :', text)
print('Pattern:', pattern)

m  re.match(pattern, text)
print('Match  :', m)
s  re.search(pattern, text)
print('Search :', s)

Поскольку буквальный текст <Код> является не отображается в начале текста ввода, он не найден с помощью Match () . Последовательность появляется два других времена в тексте, хотя, поэтому Search () находит его.

$ python3 re_match.py

Text   : This is some text -- with punctuation.
Pattern: is
Match  : None
Search : 

Метод Fullmatch () требует, чтобы вся входная строка соответствует шаблону.

re_fullmatch.py

import re

text  'This is some text -- with punctuation.'
pattern  'is'

print('Text       :', text)
print('Pattern    :', pattern)

m  re.search(pattern, text)
print('Search     :', m)
s  re.fullmatch(pattern, text)
print('Full match :', s)

Здесь Search () показывает, что шаблон отображается на входе, но он не потребляет весь вход, поэтому <код> Fullmatch () не сообщает совпадение.

$ python3 re_fullmatch.py

Text       : This is some text -- with punctuation.
Pattern    : is
Search     : 
Full match : None

поиск () Способ сборки ().

re_search_substring.py

import re

text  'This is some text -- with punctuation.'
pattern  re.compile(r'\b\w*is\w*\b')

print('Text:', text)
print()

pos  0
while True:
    match  pattern.search(text, pos)
    if not match:
        break
    s  match.start()
    e  match.end()
    print('  {:>2d} : {:>2d} = "{}"'.format(
        s, e - 1, text[s:e]))
    # Move forward in text for the next search
    pos  e

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

$ python3 re_search_substring.py

Text: This is some text -- with punctuation.

   0 :  3 = "This"
   5 :  6 = "is"

Рассекающие матчи с группами

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

re_groups.py

from re_test_patterns import test_patterns

test_patterns(
    'abbaaabbbbaaaaa',
    [('a(ab)', 'a followed by literal ab'),
     ('a(a*b*)', 'a followed by 0-n a and 0-n b'),
     ('a(ab)*', 'a followed by 0-n ab'),
     ('a(ab)+', 'a followed by 1-n ab')],
)

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

$ python3 re_groups.py

'a(ab)' (a followed by literal ab)

  'abbaaabbbbaaaaa'
  ....'aab'

'a(a*b*)' (a followed by 0-n a and 0-n b)

  'abbaaabbbbaaaaa'
  'abb'
  ...'aaabbbb'
  ..........'aaaaa'

'a(ab)*' (a followed by 0-n ab)

  'abbaaabbbbaaaaa'
  'a'
  ...'a'
  ....'aab'
  ..........'a'
  ...........'a'
  ............'a'
  .............'a'
  ..............'a'

'a(ab)+' (a followed by 1-n ab)

  'abbaaabbbbaaaaa'
  ....'aab'

Чтобы получить доступ к подстрадателям, сопоставленным отдельными группами в рамках шаблона, используйте группы () метод MATCH объекта.

re_groups_match.py

import re

text  'This is some text -- with punctuation.'

print(text)
print()

patterns  [
    (r'^(\w+)', 'word at start of string'),
    (r'(\w+)\S*$', 'word at end, with optional punctuation'),
    (r'(\bt\w+)\W+(\w+)', 'word starting with t, another word'),
    (r'(\w+t)\b', 'word ending with t'),
]

for pattern, desc in patterns:
    regex  re.compile(pattern)
    match  regex.search(text)
    print("'{}' ({})\n".format(pattern, desc))
    print('  ', match.groups())
    print()

<Код> Match.Groups () Возвращает последовательность строк в порядке групп в выражении, которое соответствует строке.

$ python3 re_groups_match.py

This is some text -- with punctuation.

'^(\w+)' (word at start of string)

   ('This',)

'(\w+)\S*$' (word at end, with optional punctuation)

   ('punctuation',)

'(\bt\w+)\W+(\w+)' (word starting with t, another word)

   ('text', 'with')

'(\w+t)\b' (word ending with t)

   ('text',)

Чтобы спросить матч одной группы, используйте метод Code> Group () . Это полезно, когда группировка используется для поиска частей строки, но некоторые части, соответствующие группами, не требуются в результатах.

re_groups_individual.py

import re

text  'This is some text -- with punctuation.'

print('Input text            :', text)

# word starting with 't' then another word
regex  re.compile(r'(\bt\w+)\W+(\w+)')
print('Pattern               :', regex.pattern)

match  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 в том, что их левая скобка появляется в выражении.

$ python3 re_groups_individual.py

Input text            : This is some text -- with punctuation.
Pattern               : (\bt\w+)\W+(\w+)
Entire match          : text -- with
Word starting with "t": text
Word after "t" word   : with

Python расширяет базовый синтаксис группировки для добавления именованные группы . Используя имена для ссылки на группы, облегчают изменение шаблона со временем, без необходимости модифицировать код с использованием результатов совпадения. Чтобы установить имя группы, используйте синтаксис <код> (? P Pattern) .

re_groups_named.py

import re

text  'This is some text -- with punctuation.'

print(text)
print()

patterns  [
    r'^(?P\w+)',
    r'(?P\w+)\S*$',
    r'(?P\bt\w+)\W+(?P\w+)',
    r'(?P\w+t)\b',
]

for pattern in patterns:
    regex  re.compile(pattern)
    match  regex.search(text)
    print("'{}'".format(pattern))
    print('  ', match.groups())
    print('  ', match.groupdict())
    print()

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

$ python3 re_groups_named.py

This is some text -- with punctuation.

'^(?P\w+)'
   ('This',)
   {'first_word': 'This'}

'(?P\w+)\S*$'
   ('punctuation',)
   {'last_word': 'punctuation'}

'(?P\bt\w+)\W+(?P\w+)'
   ('text', 'with')
   {'t_word': 'text', 'other_word': 'with'}

'(?P\w+t)\b'
   ('text',)
   {'ends_with_t': 'text'}

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

re_test_patterns_groups.py

import re


def 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.
    """
    # Look for each pattern in the text and print the results
    for pattern, desc in patterns:
        print('{!r} ({})\n'.format(pattern, desc))
        print('  {!r}'.format(text))
        for match in re.finditer(pattern, text):
            s  match.start()
            e  match.end()
            prefix  ' ' * (s)
            print(
                '  {}{!r}{} '.format(prefix,
                                     text[s:e],
                                     ' ' * (len(text) - e)),
                end' ',
            )
            print(match.groups())
            if match.groupdict():
                print('{}{}'.format(
                    ' ' * (len(text) - s),
                    match.groupdict()),
                )
        print()
    return

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

re_groups_nested.py

from re_test_patterns_groups import test_patterns

test_patterns(
    'abbaabbba',
    [(r'a((a*)(b*))', 'a followed by 0-n a and 0-n b')],
)

В этом случае группа (A *) соответствует пустой строке, поэтому возвращаемое значение из групп () включает в себя эту пустую строку в качестве соответствующего значения.

$ python3 re_groups_nested.py

'a((a*)(b*))' (a followed by 0-n a and 0-n b)

  'abbaabbba'
  'abb'        ('bb', '', 'bb')
     'aabbb'   ('abbb', 'a', 'bbb')
          'a'  ('', '', '')

Группы также полезны для указания альтернативных шаблонов. Используйте символ трубы (<код> | ), чтобы отделить два шаблона и указать, что либо образец должен совпадать. Тщательно учитывайте размещение трубы. Первое выражение в этом примере совпадает с последовательностью A , а затем последовательность, состоящая полностью из одной буквы, <код> A или B . Второй шаблон соответствует <код> A , а затем последовательность, которая может включать в себя либо или <код> b . Шаблоны похожи, но полученные совпадения совершенно разные.

re_groups_alternative.py

from re_test_patterns_groups import test_patterns

test_patterns(
    'abbaabbba',
    [(r'a((a+)|(b+))', 'a then seq. of a or seq. of b'),
     (r'a((a|b)+)', 'a then seq. of [ab]')],
)

Когда альтернативная группа не совпадает, но весь шаблон соответствует, возвращаемое значение групп () включает в себя <код> none значение в точке в последовательности, где альтернативная группа должен появиться.

$ python3 re_groups_alternative.py

'a((a+)|(b+))' (a then seq. of a or seq. of b)

  'abbaabbba'
  'abb'        ('bb', None, 'bb')
     'aa'      ('a', 'a', None)

'a((a|b)+)' (a then seq. of [ab])

  'abbaabbba'
  'abbaabbba'  ('bbaabbba', 'a')

Определение группы, содержащему субпотрунку, также полезна в тех случаях, когда строка, сопоставив SubPattern, не является частью того, что следует извлечь из полного текста. Эти виды групп называются без захвата . Не захватывающие группы могут использоваться для описания шаблонов или альтернатив повторений, без выделения соответствующей части строки в возвращении значения. Чтобы создать нехватку группы, используйте синтаксис <код> (?: Pattern) .

re_groups_noncapturing.py

from re_test_patterns_groups import test_patterns

test_patterns(
    'abbaabbba',
    [(r'a((a+)|(b+))', 'capturing form'),
     (r'a((?:a+)|(?:b+))', 'noncapturing')],
)

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

$ python3 re_groups_noncapturing.py

'a((a+)|(b+))' (capturing form)

  'abbaabbba'
  'abb'        ('bb', None, 'bb')
     'aa'      ('a', 'a', None)

'a((?:a+)|(?:b+))' (noncapturing)

  'abbaabbba'
  'abb'        ('bb',)
     'aa'      ('a',)

Опции поиска

Флаги опционов используются для изменения способа обрабатываемых подходящих двигателей выражения. Флаги могут быть объединены с использованием побитовой или операции, затем передаются в Compile () , <Код> Поиск () , <Код> Match () и другие функции которые принимают шаблон для поиска.

Нечувствительное к регистру

<Код> INGLORECASE вызывает литеральные символы и диапазоны символов в шаблоне, чтобы соответствовать как заглавными, так и строчными буквами.

re_flags_ignorecase.py

import re

text  'This is some text -- with punctuation.'
pattern  r'\bT\w+'
with_case  re.compile(pattern)
without_case  re.compile(pattern, re.IGNORECASE)

print('Text:\n  {!r}'.format(text))
print('Pattern:\n  {}'.format(pattern))
print('Case-sensitive:')
for match in with_case.findall(text):
    print('  {!r}'.format(match))
print('Case-insensitive:')
for match in without_case.findall(text):
    print('  {!r}'.format(match))

Поскольку шаблон включает в себя буквальный <код> T , если INGLORECASE не устанавливается, единственное совпадение – это слово <код> это . Когда случай игнорируется, <код> текст также совпадает.

$ python3 re_flags_ignorecase.py

Text:
  'This is some text -- with punctuation.'
Pattern:
  \bT\w+
Case-sensitive:
  'This'
Case-insensitive:
  'This'
  'text'

Вход с несколькими строками

Два флага влияют на поиск в многострочных работах: <Код> Многолина и <Код> dotall . Флаг <Код> MultiLine Управляет тем, как код сопоставления обрабатывающих шаблон обрабатывает инструкции закрепления для текста, содержащих новинку символов. Когда включен многострочный режим, правила якора для ^ и <код> $ применяются в начале и конце каждой строки, в дополнение к всей строке.

re_flags_multiline.py

import re

text  'This is some text -- with punctuation.\nA second line.'
pattern  r'(^\w+)|(\w+\S*$)'
single_line  re.compile(pattern)
multiline  re.compile(pattern, re.MULTILINE)

print('Text:\n  {!r}'.format(text))
print('Pattern:\n  {}'.format(pattern))
print('Single Line :')
for match in single_line.findall(text):
    print('  {!r}'.format(match))
print('Multline    :')
for match in multiline.findall(text):
    print('  {!r}'.format(match))

Узор в примере совпадает с первым или последним словом ввода. Это соответствует линию . В конце строки, даже если нет Newline.

$ python3 re_flags_multiline.py

Text:
  'This is some text -- with punctuation.\nA second line.'
Pattern:
  (^\w+)|(\w+\S*$)
Single Line :
  ('This', '')
  ('', 'line.')
Multline    :
  ('This', '')
  ('', 'punctuation.')
  ('A', '')
  ('', 'line.')

<Код> dotall – другой флаг, связанный с многослойным текстом. Обычно точка символ (<код>. ) соответствует всему входным тексту, кроме символа Newline. Флаг позволяет точеству соответствовать новым линиям.

re_flags_dotall.py

import re

text  'This is some text -- with punctuation.\nA second line.'
pattern  r'.+'
no_newlines  re.compile(pattern)
dotall  re.compile(pattern, re.DOTALL)

print('Text:\n  {!r}'.format(text))
print('Pattern:\n  {}'.format(pattern))
print('No newlines :')
for match in no_newlines.findall(text):
    print('  {!r}'.format(match))
print('Dotall      :')
for match in dotall.findall(text):
    print('  {!r}'.format(match))

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

$ python3 re_flags_dotall.py

Text:
  'This is some text -- with punctuation.\nA second line.'
Pattern:
  .+
No newlines :
  'This is some text -- with punctuation.'
  'A second line.'
Dotall      :
  'This is some text -- with punctuation.\nA second line.'

Unicode

Под Python 3, STR объекты используют полный набор символов Unicode, а регулярные выражения выражения на STR предполагают, что текст шаблона и входного сигнала являются оба Unicode. Коды побега, описанные ранее, определены в терминах Unicode по умолчанию. Эти предположения означают, что шаблон \ W + будет соответствовать как слова «Французский» и «Français». Чтобы ограничить коды Escape к набору символов ASCII, как это было значение по умолчанию в Python 2, используйте флаг <код> ASCII при компиляции шаблона или при вызове функций уровня модуля <код> Поиск () и <код> совпадают () .

re_flags_ascii.py

import re

text  u'Français złoty Österreich'
pattern  r'\w+'
ascii_pattern  re.compile(pattern, re.ASCII)
unicode_pattern  re.compile(pattern)

print('Text    :', text)
print('Pattern :', pattern)
print('ASCII   :', list(ascii_pattern.findall(text)))
print('Unicode :', list(unicode_pattern.findall(text)))

Другие escape Sequences (<код> \ W , <код> \ b , <код> \ b , <код> \ d , <код> \ d , <код> \ s , а <код> \ s ) также обрабатываются по-разному для текста ASCII. Вместо того, чтобы консультироваться с базой данных Unicode, чтобы найти свойства каждого символа, Re использует определение ASCII набора символов.

$ python3 re_flags_ascii.py

Text    : Français złoty Österreich
Pattern : \w+
ASCII   : ['Fran', 'ais', 'z', 'oty', 'sterreich']
Unicode : ['Français', 'złoty', 'Österreich']

Синтаксис выражения Verbose

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

Узор для проверки адресов электронной почты проиллюстрирует, как режим Verbose облегчает работу с регулярными выражениями. Первая версия распознает адреса, которые заканчиваются одним из трех доменов верхнего уровня: <Код> .com , <Код> .org или <код> .edu .

re_email_compact.py

import re

address  re.compile('[\w\d.+-]+@([\w\d.]+\.)+(com|org|edu)')

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:
    match  address.search(candidate)
    print('{:<30}  {}'.format(
        candidate, 'Matches' if match else 'No match')
    )

Это выражение уже сложно. Есть несколько классов персонажей, групп и повторений повторений.

$ python3 re_email_compact.py

first.last@example.com          Matches
first.last+category@gmail.com   Matches
valid-address@mail.example.com  Matches
not-valid@example.foo           No match

Преобразование выражения в более многословный формат облегчит пропустить.

re_email_verbose.py

import re

address  re.compile(
    '''
    [\w\d.+-]+       # username
    @
    ([\w\d.]+\.)+    # domain name prefix
    (com|org|edu)    # TODO: support more top-level domains
    ''',
    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:
    match  address.search(candidate)
    print('{:<30}  {}'.format(
        candidate, 'Matches' if match else 'No match'),
    )

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

$ python3 re_email_verbose.py

first.last@example.com          Matches
first.last+category@gmail.com   Matches
valid-address@mail.example.com  Matches
not-valid@example.foo           No match

Эта расширенная версия Parses входы, которые включают имя человека и адрес электронной почты, как может появиться в заголовке электронной почты. Название начнется сначала и стоит самостоятельно, и адрес электронной почты следует, окруженные угловыми скобками (<код> < и <код >> ).

re_email_with_name.py

import re

address  re.compile(
    '''

    # A name is made up of letters, and may include "."
    # for title abbreviations and middle initials.
    ((?P
       ([\w.,]+\s+)*[\w.,]+)
       \s*
       # Email addresses are wrapped in angle
       # brackets < >, but only if a name is
       # found, so keep the start bracket in this
       # group.
       <
    )? # the entire name is optional

    # The address itself: username@domain.tld
    (?P
      [\w\d.+-]+       # username
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
    )

    >? # optional closing angle bracket
    ''',
    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 ',
    u'No Brackets first.last@example.com',
    u'First Last',
    u'First Middle Last ',
    u'First M. Last ',
    u'',
]

for candidate in candidates:
    print('Candidate:', candidate)
    match  address.search(candidate)
    if match:
        print('  Name :', match.groupdict()['name'])
        print('  Email:', match.groupdict()['email'])
    else:
        print('  No match')

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

$ python3 re_email_with_name.py

Candidate: first.last@example.com
  Name : None
  Email: first.last@example.com
Candidate: first.last+category@gmail.com
  Name : None
  Email: first.last+category@gmail.com
Candidate: valid-address@mail.example.com
  Name : None
  Email: valid-address@mail.example.com
Candidate: not-valid@example.foo
  No match
Candidate: First Last 
  Name : First Last
  Email: first.last@example.com
Candidate: No Brackets first.last@example.com
  Name : None
  Email: first.last@example.com
Candidate: First Last
  No match
Candidate: First Middle Last 
  Name : First Middle Last
  Email: first.last@example.com
Candidate: First M. Last 
  Name : First M. Last
  Email: first.last@example.com
Candidate: 
  Name : None
  Email: first.last@example.com

Флаги встраивания в шаблонах

В ситуациях, когда флаги не могут быть добавлены при компиляции выражения, например, когда шаблон пропускается как аргумент к функции библиотеки, которая будет сознавать ее позже, флаги могут быть встроены внутри сама строки экспрессии. Например, чтобы включить сопоставление нечувствительности к регистру, добавьте <код> (? I) к началу выражения.

re_flags_embedded.py

import re

text  'This is some text -- with punctuation.'
pattern  r'(?i)\bT\w+'
regex  re.compile(pattern)

print('Text      :', text)
print('Pattern   :', pattern)
print('Matches   :', regex.findall(text))

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

$ python3 re_flags_embedded.py

Text      : This is some text -- with punctuation.
Pattern   : (?i)\bT\w+
Matches   : ['This', 'text']

Сокращения для всех флагов перечислены в таблице ниже.

Устойчивость флага регулярного выражения

Флаг

Сокращение

Ascii

INGLORECASE.

Многолетний

Дозал

ПОДРОБНЫЙ

Встроенные флаги могут быть объединены, размещая их в одной группе. Например, <Код> (? IM) включает в себя, нечувствительно к регистру для многослойных строк.

Глядя вперед или позади

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

re_look_ahead.py

import re

address  re.compile(
    '''
    # A name is made up of letters, and may include "."
    # for title abbreviations and middle initials.
    ((?P
       ([\w.,]+\s+)*[\w.,]+
     )
     \s+
    ) # name is no longer optional

    # LOOKAHEAD
    # Email addresses are wrapped in angle brackets, but only
    # if both are present or neither is.
   >$)       # remainder wrapped in angle brackets
        |
        ([^<].*[^>]$) # remainder *not* wrapped in angle brackets
      )

    
      [\w\d.+-]+       # username
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
    )

    >? # optional closing angle bracket
    ''',
    re.VERBOSE)

candidates  [
    u'First Last ',
    u'No Brackets first.last@example.com',
    u'Open Bracket ',
]

for candidate in candidates:
    print('Candidate:', candidate)
    match  address.search(candidate)
    if match:
        print('  Name :', match.groupdict()['name'])
        print('  Email:', match.groupdict()['email'])
    else:
        print('  No match')

Существует несколько важных изменений в этой версии выражения. Во-первых, часть имени больше не является необязательной. Это означает, что автономные адреса не совпадают, но он также предотвращает неправильное отформатированное имя/адресные комбинации. Положительный взгляд вперед после того, как группа «Имя» утверждает, что либо остальная часть строки завернута с парой угловых кронштейнов, либо не существует несоответствующего кронштейна; либо оба из, либо ни одно из скобок присутствуют. Впереди ожидаемый образ выражен в группе, но матч для поиска впереди группы не употребляет ни один из входных текстов, поэтому остальная часть шаблона поднимается с того же места после того, как появился соответствующий матч.

$ python3 re_look_ahead.py

Candidate: First Last 
  Name : First Last
  Email: first.last@example.com
Candidate: No Brackets first.last@example.com
  Name : No Brackets
  Email: first.last@example.com
Candidate: Open Bracket 
  No match

Отрицательный вид впереди Assertion (<код> (?! Pattern) ) говорит, что шаблон не соответствует тексту после текущей точки. Например, шаблон распознавания электронного письма может быть изменен для игнорирования адресов адресов , обычно используемых автоматическими системами.

re_negative_look_ahead.py

import re

address  re.compile(
    '''
    ^

    # An address: username@domain.tld

    # Ignore noreply addresses
    (?!noreply@.*$)

    [\w\d.+-]+       # username
    @
    ([\w\d.]+\.)+    # domain name prefix
    (com|org|edu)    # limit the allowed top-level domains

    $
    ''',
    re.VERBOSE)

candidates  [
    u'first.last@example.com',
    u'noreply@example.com',
]

for candidate in candidates:
    print('Candidate:', candidate)
    match  address.search(candidate)
    if match:
        print('  Match:', candidate[match.start():match.end()])
    else:
        print('  No match')

Адрес, начиная с NOREPLOPLY не соответствует шаблону, поскольку утверждение в будущем не удается.

$ python3 re_negative_look_ahead.py

Candidate: first.last@example.com
  Match: first.last@example.com
Candidate: noreply@example.com
  No match

Вместо того, чтобы смотреть вперед для NOREPLOPLY в части имени пользователя адреса электронной почты, шаблон может быть альтернативно написан с использованием отрицательного взгляда после того, как имя пользователя соответствует синтаксису < код> (? .

re_negative_look_behind.py

import re

address  re.compile(
    '''
    ^

    # An address: username@domain.tld

    [\w\d.+-]+       # username

    # Ignore noreply addresses
    (?

Глядя назад Работает немного по-другому, чем осмотр вперед, в том, что выражение должно использовать рисунок фиксированной длины. Повторения допускаются, пока есть фиксированное количество из них (никаких подстановочных знаков или диапазонов).

$ python3 re_negative_look_behind.py

Candidate: first.last@example.com
  Match: first.last@example.com
Candidate: noreply@example.com
  No match

Положительный взгляд позади может использоваться для поиска текста после шаблона с использованием синтаксиса <код> . В следующем примере выражение находит ручки Twitter.

re_look_behind.py

import re

twitter  re.compile(
    '''
    # A twitter handle: @username
   >
    ([\w\d_]+)       # username
    ''',
    re.VERBOSE)

text  '''This text includes two Twitter handles.
One for @ThePSF, and one for the author, @doughellmann.
'''

print(text)
for match in twitter.findall(text):
    print('Handle:', match)

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

$ python3 re_look_behind.py

This text includes two Twitter handles.
One for @ThePSF, and one for the author, @doughellmann.

Handle: ThePSF
Handle: doughellmann

Выражения самосвязки

Соответствующие значения могут использоваться в более поздних частях выражения. Например, пример электронной почты может быть обновлен, чтобы соответствовать только адресам, состоящим из первых и фамилию человека, включая обратные ссылки на эти группы. Самый простой способ добиться этого – ссылаясь на ранее сопоставленную группу по идентификационному номеру, используя \ NUM .

re_refer_to_group.py

import re

address  re.compile(
    r'''

    # The regular name
    (\w+)               # first name
    \s+
    (([\w.]+)\s+)?      # optional middle name or initial
    (\w+)               # last name

    \s+

    <

    # The address: first_name.last_name@domain.tld
    (?P
      \1               # first name
      \.
      \4               # last name
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
    )

    >
    ''',
    re.VERBOSE | re.IGNORECASE)

candidates  [
    u'First Last ',
    u'Different Name ',
    u'First Middle Last ',
    u'First M. Last ',
]

for candidate in candidates:
    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 ссылок, используя стандартный синтаксис ссылки \ N , потому что если идентификационный номер длиной – это три цифры, он будет интерпретировать как значение восьмерикатора, а не в группу ссылка. Конечно, если в выражении насчитывается более 99 групп, будут более серьезные проблемы обслуживания, чем просто не иметь возможности ссылаться на все они.

$ python3 re_refer_to_group.py

Candidate: First Last 
  Match name : First Last
  Match email: first.last@example.com
Candidate: Different Name 
  No match
Candidate: First Middle Last 
  Match name : First Last
  Match email: first.last@example.com
Candidate: First M. Last 
  Match name : First Last
  Match email: first.last@example.com

Physer Python Parser включает в себя расширение, которое использует <код> для обозначения значения именованной группы, согласованной ранее в выражении.

re_refer_to_named_group.py

import re

address  re.compile(
    '''

    # The regular name
    (?P\w+)
    \s+
    (([\w.]+)\s+)?      # optional middle name or initial
    (?P\w+)

    \s+

    <

    # The address: first_name.last_name@domain.tld
    (?P
     >
      \.
     >
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
    )

    >
    ''',
    re.VERBOSE | re.IGNORECASE)

candidates  [
    u'First Last ',
    u'Different Name ',
    u'First Middle Last ',
    u'First M. Last ',
]

for candidate in candidates:
    print('Candidate:', candidate)
    match  address.search(candidate)
    if match:
        print('  Match name :', match.groupdict()['first_name'],
              end' ')
        print(match.groupdict()['last_name'])
        print('  Match email:', match.groupdict()['email'])
    else:
        print('  No match')

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

$ python3 re_refer_to_named_group.py

Candidate: First Last 
  Match name : First Last
  Match email: first.last@example.com
Candidate: Different Name 
  No match
Candidate: First Middle Last 
  Match name : First Last
  Match email: first.last@example.com
Candidate: First M. Last 
  Match name : First Last
  Match email: first.last@example.com

Другой механизм использования обратных ссылок в выражениях выбирает другой шаблон на основе того, соответствует ли предыдущей группе. Шаблон электронной почты может быть скорректирован так, чтобы угловые скобки необходимы, если имя присутствует и не требуется, если адрес электронной почты сам по себе. Синтаксис для тестирования, если группа согласилась, это <код> (? (ID) YES-Expression | NO-Expression) , где <код> ID – это имя группы или число, <код > YES-Expression – это шаблон, используемый, если группа имеет значение, и <код> без выражения – это шаблон для использования в противном случае.

re_id.py

import re

address  re.compile(
    '''
    ^

    # A name is made up of letters, and may include "."
    # for title abbreviations and middle initials.
    (?P
       ([\w.]+\s+)*[\w.]+
     )?
    \s*

    # Email addresses are wrapped in angle brackets, but
    # only if a name is found.
    (?(name)
      # remainder wrapped in angle brackets because
      # there is a name
     >$)))
      |
      # remainder does not include angle brackets without name
     >]$))
     )

    # Look for a bracket only if the look-ahead assertion
    # found both of them.
    (?(brackets)<|\s*)

    # The address itself: username@domain.tld
    (?P
      [\w\d.+-]+       # username
      @
      ([\w\d.]+\.)+    # domain name prefix
      (com|org|edu)    # limit the allowed top-level domains
     )

    # Look for a bracket only if the look-ahead assertion
    # found both of them.
    (?(brackets)>|\s*)

    $
    ''',
    re.VERBOSE)

candidates  [
    u'First Last ',
    u'No Brackets first.last@example.com',
    u'Open Bracket ',
    u'no.brackets@example.com',
]

for candidate in candidates:
    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')

Эта версия интерсера адреса электронной почты использует два теста. Если имя MARGE GROUP MARGE, то внешнее утверждение внешнего вида требует как угловых кронштейнов, и устанавливает <код> скобки . Если имя не совпадает, утверждение требует, чтобы остальная часть текста не обладает угловыми кронштейнами вокруг него. Позже, если установлен кронштейны Group, фактический код сопоставления шаблона потребляет кронштейны на входе с использованием буквальных шаблонов; В противном случае он потребляет любое место.

$ python3 re_id.py

Candidate: First Last 
  Match name : First Last
  Match email: first.last@example.com
Candidate: No Brackets first.last@example.com
  No match
Candidate: Open Bracket 
  No match
Candidate: no.brackets@example.com
  Match name : None
  Match email: no.brackets@example.com

Изменение строк с узорами

В дополнение к поиску через текст, Re поддерживает модификацию текста, используя регулярные выражения в качестве механизма поиска, и замены могут ссылочные группы сопоставить в шаблоне в рамках текста замены. Используйте SUB () для замены всех вхождений шаблона с другой строкой.

re_sub.py

import re

bold  re.compile(r'\*{2}(.*?)\*{2}')

text  'Make this **bold**.  This **too**.'

print('Text:', text)
print('Bold:', bold.sub(r'\1', text))

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

$ python3 re_sub.py

Text: Make this **bold**.  This **too**.
Bold: Make this bold.  This too.

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

re_sub_named_groups.py

import re

bold  re.compile(r'\*{2}(?P.*?)\*{2}')

text  'Make this **bold**.  This **too**.'

print('Text:', text)
print('Bold:', bold.sub(r'\g', text))

\ G <ИМЯ> Syntax также работает с пронумерованными ссылками, и использование его устраняет любую двусмысленность между номерами группы и окружающими буквальными цифрами.

$ python3 re_sub_named_groups.py

Text: Make this **bold**.  This **too**.
Bold: Make this bold.  This too.

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

re_sub_count.py

import re

bold  re.compile(r'\*{2}(.*?)\*{2}')

text  'Make this **bold**.  This **too**.'

print('Text:', text)
print('Bold:', bold.sub(r'\1', text, count1))

Только первая замена сделана, потому что Count <код> 1 .

$ python3 re_sub_count.py

Text: Make this **bold**.  This **too**.
Bold: Make this bold.  This **too**.

SUBN () работает так же, как <код> sub () , за исключением того, что он возвращает как модифицированную строку, так и подсчету заседаний.

re_subn.py

import re

bold  re.compile(r'\*{2}(.*?)\*{2}')

text  'Make this **bold**.  This **too**.'

print('Text:', text)
print('Bold:', bold.subn(r'\1', text))

Шаблон поиска совпадает дважды в примере.

$ python3 re_subn.py

Text: Make this **bold**.  This **too**.
Bold: ('Make this bold.  This too.', 2)

Расщепление с узорами

STR.SPLIT () является одним из наиболее часто используемых методов для разрыва строк для их разбора. Он поддерживает только использование буквальных значений в качестве сепараторов, а иногда и регулярное выражение необходимо, если вход не является последовательно отформатированным. Например, многие языки простых текстовых разметков определяют сепараторы абзаца в качестве двух или более новой линии (<код> \ N ). В этом случае STR.SPLIT () нельзя использовать из-за «или более» части определения.

Стратегия выявления абзацев с использованием FACHALL () будет использовать шаблон, такой как <код> (. +?) \ N {2,} .

re_paragraphs_findall.py

import re

text  '''Paragraph one
on two lines.

Paragraph two.


Paragraph three.'''

for num, para in enumerate(re.findall(r'(.+?)\n{2,}',
                                      text,
                                      flagsre.DOTALL)
                           ):
    print(num, repr(para))
    print()

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

$ python3 re_paragraphs_findall.py

0 'Paragraph one\non two lines.'

1 'Paragraph two.'

Расширяя шаблон, чтобы сказать, что абзац заканчивается двумя или более двумя вводами или конец ввода исправляет проблему, но делает шаблон более сложным. Преобразование в RE.SPLIT () Вместо re.findall () обрабатывает граничное условие автоматически и сохраняет упрощенный урок.

re_split.py

import re

text  '''Paragraph one
on two lines.

Paragraph two.


Paragraph three.'''

print('With findall:')
for num, para in enumerate(re.findall(r'(.+?)(\n{2,}|$)',
                                      text,
                                      flagsre.DOTALL)):
    print(num, repr(para))
    print()

print()
print('With split:')
for num, para in enumerate(re.split(r'\n{2,}', text)):
    print(num, repr(para))
    print()

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

$ python3 re_split.py

With findall:
0 ('Paragraph one\non two lines.', '\n\n')

1 ('Paragraph two.', '\n\n\n')

2 ('Paragraph three.', '')


With split:
0 'Paragraph one\non two lines.'

1 'Paragraph two.'

2 'Paragraph three.'

Включение выражения в скобках для определения группы вызывает группу SPLIT () для работы больше похожи на str.Partition () , поэтому он возвращает значения сепаратора, а также другие части Струна.

re_split_groups.py

import re

text  '''Paragraph one
on two lines.

Paragraph two.


Paragraph three.'''

print('With split:')
for num, para in enumerate(re.split(r'(\n{2,})', text)):
    print(num, repr(para))
    print()

Выход теперь включает в себя каждый абзац, а также последовательность новых линий, отделяющих их.

$ python3 re_split_groups.py

With split:
0 'Paragraph one\non two lines.'

1 '\n\n'

2 'Paragraph two.'

3 '\n\n\n'

4 'Paragraph three.'

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