Поиск…


Вступление

Python предоставляет регулярные выражения через модуль re .

Регулярные выражения - это комбинации символов, которые интерпретируются как правила для подстановки подстрок. Например, выражение 'amount\D+\d+' будет соответствовать любой строке, состоящей из amount слова плюс целое число, разделенное одной или несколькими цифрами, например: amount=100 , amount is 3 , amount is equal to: 33 и т. Д.

Синтаксис

  • Прямые регулярные выражения

  • re.match (pattern, string, flag = 0) # Out: шаблон соответствия в начале строки или None

  • re.search (pattern, string, flag = 0) # Out: шаблон соответствия внутри строки или None

  • re.findall (pattern, string, flag = 0) # Out: список всех совпадений шаблона в строке или []

  • re.finditer (pattern, string, flag = 0) # Out: то же, что и re.findall, но возвращает объект итератора

  • re.sub (pattern, replacement, string, flag = 0) # Out: строка с заменой (строка или функция) вместо шаблона

  • Предварительно скомпилированные регулярные выражения

  • precompiled_pattern = re.compile (pattern, flag = 0)

  • precompiled_pattern.match (строка) # Out: совпадение в начале строки или None

  • precompiled_pattern.search (строка) # Out: соответствие в любом месте строки или None

  • precompiled_pattern.findall (строка) # Out: список всех соответствующих подстрок

  • precompiled_pattern.sub (строка / шаблон / функция, строка) # Out: заменена строка

Соответствие началу строки

Первый аргумент re.match() - это регулярное выражение, второе - строка, которая соответствует:

import re

pattern = r"123"
string = "123zzb"

re.match(pattern, string)
# Out: <_sre.SRE_Match object; span=(0, 3), match='123'>

match = re.match(pattern, string)

match.group()
# Out: '123'

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

Строковый литерал имеет немного иной синтаксис, чем строковый литерал, а именно обратная косая черта \ в строковом литерале означает «просто обратную косую черту», ​​и нет необходимости в удвоении задержек, чтобы избежать «escape-последовательностей», таких как новые строки ( \n ) , tabs ( \t ), backspaces ( \ ), form-feeds ( \r ) и т. д. В обычных строковых литералах каждая обратная косая черта должна быть удвоена, чтобы не быть принятой за начало escape-последовательности.

Следовательно, r"\n" представляет собой строку из двух символов: \ и n . В шаблонах регулярных выражений также используются обратные косые черты, например, \d относится к любому символу цифры. Мы можем избежать двойного выхода из наших строк ( "\\d" ), используя необработанные строки ( r"\d" ).

Например:

string = "\\t123zzb" # here the backslash is escaped, so there's no tab, just '\' and 't'
pattern = "\\t123"   # this will match \t (escaping the backslash) followed by 123
re.match(pattern, string).group()   # no match
re.match(pattern, "\t123zzb").group()  # matches '\t123'

pattern = r"\\t123"  
re.match(pattern, string).group()   # matches '\\t123'

Согласование выполняется только с начала строки. Если вы хотите re.search любом месте, используйте re.search :

match = re.match(r"(123)", "a123zzb")

match is None
# Out: True

match = re.search(r"(123)", "a123zzb")

match.group()
# Out: '123'

поиск

pattern = r"(your base)"
sentence = "All your base are belong to us."

match = re.search(pattern, sentence)
match.group(1)
# Out: 'your base'

match = re.search(r"(belong.*)", sentence)
match.group(1)
# Out: 'belong to us.'

Поиск выполняется в любом месте строки в отличие от re.match . Вы также можете использовать re.findall .

Вы также можете выполнить поиск в начале строки (используйте ^ ),

match = re.search(r"^123", "123zzb")
match.group(0)
# Out: '123'

match = re.search(r"^123", "a123zzb")
match is None
# Out: True

в конце строки (используйте $ ),

match = re.search(r"123$", "zzb123")
match.group(0)
# Out: '123'

match = re.search(r"123$", "123zzb")
match is None
# Out: True

или оба (используйте как ^ и $ ):

match = re.search(r"^123$", "123")
match.group(0)
# Out: '123'

группирование

Группировка выполняется с помощью круглых скобок. Calling group() возвращает строку, сформированную из соответствующих подгрупп в скобках.

match.group() # Group without argument returns the entire match found
# Out: '123'
match.group(0) # Specifying 0 gives the same result as specifying no argument
# Out: '123'

Аргументы также могут быть предоставлены group() для извлечения определенной подгруппы.

Из документов :

Если есть один аргумент, результатом будет одиночная строка; если есть несколько аргументов, результатом является кортеж с одним элементом для каждого аргумента.

С другой стороны, вызывающие groups() возвращают список кортежей, содержащих подгруппы.

sentence = "This is a phone number 672-123-456-9910"
pattern = r".*(phone).*?([\d-]+)"

match = re.match(pattern, sentence)

match.groups()   # The entire match as a list of tuples of the paranthesized subgroups
# Out: ('phone', '672-123-456-9910')

m.group()        # The entire match as a string
# Out: 'This is a phone number 672-123-456-9910'

m.group(0)       # The entire match as a string
# Out: 'This is a phone number 672-123-456-9910'

m.group(1)       # The first parenthesized subgroup.
# Out: 'phone'

m.group(2)       # The second parenthesized subgroup.
# Out: '672-123-456-9910'

m.group(1, 2)    # Multiple arguments give us a tuple.
# Out: ('phone', '672-123-456-9910')

Именованные группы

match = re.search(r'My name is (?P<name>[A-Za-z ]+)', 'My name is John Smith')
match.group('name')
# Out: 'John Smith'

match.group(1)
# Out: 'John Smith'

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

Нехватывающие группы

Использование (?:) создает группу, но группа не захватывается. Это означает, что вы можете использовать его как группу, но это не будет загрязнять ваше «групповое пространство».

re.match(r'(\d+)(\+(\d+))?', '11+22').groups()
# Out: ('11', '+22', '22')

re.match(r'(\d+)(?:\+(\d+))?', '11+22').groups()
# Out: ('11', '22')

Этот пример соответствует 11+22 или 11 , но не 11+ . Это связано с тем, что знак + и второй член сгруппированы. С другой стороны, знак + не фиксируется.

Экранирование специальных символов

Специальные символы (например, скобки символьного класса [ и ] ниже) не соответствуют буквально:

match = re.search(r'[b]', 'a[b]c')
match.group()
# Out: 'b'

Удерживая специальные символы, их можно сопоставить буквально:

match = re.search(r'\[b\]', 'a[b]c')
match.group()
# Out: '[b]'

Для re.escape() можно использовать функцию re.escape() :

re.escape('a[b]c')
# Out: 'a\\[b\\]c'
match = re.search(re.escape('a[b]c'), 'a[b]c')
match.group()
# Out: 'a[b]c'

Функция re.escape() все специальные символы, поэтому полезно, если вы составляете регулярное выражение, основанное на пользовательском вводе:

username = 'A.C.'  # suppose this came from the user
re.findall(r'Hi {}!'.format(username), 'Hi A.C.! Hi ABCD!')
# Out: ['Hi A.C.!', 'Hi ABCD!']
re.findall(r'Hi {}!'.format(re.escape(username)), 'Hi A.C.! Hi ABCD!')
# Out: ['Hi A.C.!']

Замена

Замены могут быть выполнены в строках с использованием re.sub .

Замена строк

re.sub(r"t[0-9][0-9]", "foo", "my name t13 is t44 what t99 ever t44")
# Out: 'my name foo is foo what foo ever foo'

Использование ссылок на группы

Замены с небольшим количеством групп могут быть сделаны следующим образом:

re.sub(r"t([0-9])([0-9])", r"t\2\1", "t13 t19 t81 t25")
# Out: 't31 t91 t18 t52'

Однако, если вы делаете идентификатор группы, такой как «10», это не работает : \10 читается как «Идентификационный номер 1, за которым следует 0». Поэтому вы должны быть более конкретными и использовать обозначение \g<i> :

re.sub(r"t([0-9])([0-9])", r"t\g<2>\g<1>", "t13 t19 t81 t25")
# Out: 't31 t91 t18 t52'

Использование функции замены

items = ["zero", "one", "two"]
re.sub(r"a\[([0-3])\]", lambda match: items[int(match.group(1))], "Items: a[0], a[1], something, a[2]")
# Out: 'Items: zero, one, something, two'

Найти все неперекрывающиеся соответствия

re.findall(r"[0-9]{2,3}", "some 1 text 12 is 945 here 4445588899")
# Out: ['12', '945', '444', '558', '889']

Обратите внимание, что r до "[0-9]{2,3}" говорит python интерпретировать строку as-is; как «сырую» строку.

Вы также можете использовать re.finditer() который работает так же, как и re.findall() но возвращает итератор с объектами SRE_Match вместо списка строк:

results = re.finditer(r"([0-9]{2,3})", "some 1 text 12 is 945 here 4445588899")
print(results)
# Out: <callable-iterator object at 0x105245890>
for result in results:
     print(result.group(0))
''' Out:
12
945
444
558
889
'''

Предварительно скомпилированные шаблоны

import re

precompiled_pattern = re.compile(r"(\d+)")
matches = precompiled_pattern.search("The answer is 41!")
matches.group(1)
# Out: 41

matches = precompiled_pattern.search("Or was it 42?")
matches.group(1)
# Out: 42

Компиляция шаблона позволяет повторно использовать его в программе. Однако обратите внимание, что Python кэширует недавно используемые выражения ( docs , SO ответ ), поэтому «программы, которые используют только несколько регулярных выражений одновременно, не должны беспокоиться о компиляции регулярных выражений» .

import re

precompiled_pattern = re.compile(r"(.*\d+)")
matches = precompiled_pattern.match("The answer is 41!")
print(matches.group(1))
# Out: The answer is 41

matches = precompiled_pattern.match("Or was it 42?")
print(matches.group(1))
# Out: Or was it 42

Его можно использовать с re.match ().

Проверка допустимых символов

Если вы хотите проверить, что строка содержит только определенный набор символов, в этом случае az, AZ и 0-9, вы можете сделать это так,

import re

def is_allowed(string):
    characherRegex = re.compile(r'[^a-zA-Z0-9.]')
    string = characherRegex.search(string)
    return not bool(string)
    
print (is_allowed("abyzABYZ0099")) 
# Out: 'True'

print (is_allowed("#*@#$%^")) 
# Out: 'False'

Вы также можете адаптировать строку выражения от [^a-zA-Z0-9.] [^a-z0-9.] , Чтобы, например, запретить прописные буквы.

Частичный кредит: http://stackoverflow.com/a/1325265/2697955

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

Вы также можете использовать регулярные выражения для разделения строки. Например,

import re
data = re.split(r'\s+', 'James 94 Samantha 417 Scarlett 74')
print( data )
# Output: ['James', '94', 'Samantha', '417', 'Scarlett', '74']

Флаги

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

Ключевое слово Flags

Ниже пример для re.search но он работает для большинства функций модуля re .

m = re.search("b", "ABC")  
m is None
# Out: True

m = re.search("b", "ABC", flags=re.IGNORECASE)
m.group()
# Out: 'B'

m = re.search("a.b", "A\nBC", flags=re.IGNORECASE) 
m is None
# Out: True

m = re.search("a.b", "A\nBC", flags=re.IGNORECASE|re.DOTALL) 
m.group()
# Out: 'A\nB'

Общие флаги

Флаг Краткое описание
re.IGNORECASE , re.I Заставляет шаблон игнорировать случай
re.DOTALL , re.S Делает . соответствовать всем, включая символы новой строки
re.MULTILINE , re.M Делает ^ совпадающим начало строки и $ конец строки
re.DEBUG Включает отладочную информацию

Полный список всех доступных флагов проверяет документы

Встроенные флаги

Из документов :

(?iLmsux) (одна или несколько букв из набора «i», «L», «m», «s», «u», «x».)

Группа соответствует пустой строке; буквы устанавливают соответствующие флаги: re.I (игнорировать регистр), re.L (зависит от локали), re.M (многострочный), re.S (точка соответствует всем), re.U (зависит от Юникода) и re.X (verbose), для всего регулярного выражения. Это полезно, если вы хотите включить флаги как часть обычного выражения, вместо передачи аргумента флага функции re.compile ().

Обратите внимание, что флаг (? X) изменяет способ анализа выражения. Он должен использоваться сначала в строке выражения или после одного или нескольких символов пробелов. Если перед флагом есть символы без пробелов, результаты не определены.

Итерация по совпадениям с использованием `re.finditer`

Вы можете использовать re.finditer для re.finditer всех совпадений в строке. Это дает вам (по сравнению с дополнительной информацией re.findall , такой как информация о местоположении соответствия в строке (индексы):

import re
text = 'You can try to find an ant in this string'
pattern = 'an?\w' # find 'an' either with or without a following word character

for match in re.finditer(pattern, text):
    # Start index of match (integer)
    sStart = match.start()

    # Final index of match (integer)
    sEnd = match.end()

    # Complete match (string)
    sGroup = match.group()

    # Print match
    print('Match "{}" found at: [{},{}]'.format(sGroup, sStart,sEnd))

Результат:

Match "an" found at: [5,7]
Match "an" found at: [20,22]
Match "ant" found at: [23,26]

Соответствовать выражению только в определенных местах

Часто вы хотите сопоставить выражение только в определенных местах (оставив их нетронутыми в других, то есть). Рассмотрим следующее предложение:

An apple a day keeps the doctor away (I eat an apple everyday).

Здесь «яблоко» происходит дважды, что можно решить с помощью так называемых контрольных глаголов backtracking, которые поддерживаются новым модулем regex . Идея такова:

forget_this | or this | and this as well | (but keep this)

С нашим примером яблока это будет:

import regex as re
string = "An apple a day keeps the doctor away (I eat an apple everyday)."
rx = re.compile(r'''
    \([^()]*\) (*SKIP)(*FAIL)  # match anything in parentheses and "throw it away"
    |                          # or
    apple                      # match an apple
    ''', re.VERBOSE)
apples = rx.findall(string)
print(apples)
# only one

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


Вот как это работает:
  • При просмотре слева направо двигатель регулярного выражения потребляет все влево, (*SKIP) действует как «всегда-истинное утверждение». Впоследствии он корректно выходит из строя (*FAIL) и возвращается.
  • Теперь он доходит до точки (*SKIP) справа налево (ака, в то время как возвращается), где запрещено идти дальше влево. Вместо этого двигателю предлагается выбросить что-либо влево и перейти к точке, где был вызван (*SKIP) .


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow