Регулярные выражения (Regex) в Python

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

Первый аргумент 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 , что указывает на то, что строка является исходным строка символов.

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

Следовательно, r"\n" является строкой из 2 -х символов: \ и n.Regex модель также использовать обратную косую черту, например , \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 вместо:

 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' 

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

Группировка осуществляется с помощью скобок. Вызов 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('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 интерпретировать строку как есть; как «сырая» строка.

Вы можете также использовать 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 кэширует недавно использованные выражения ( документы , 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.] , Чтобы запретить прописные буквы, например.

Частичный кредит: https://codecamp.ru/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 ключевого слова или непосредственно в выражении.

Ключевое слово флаги

Ниже приведен пример для 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', 'м', 'S', 'и', 'х'.)

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

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

Перебор совпадений с использованием `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).

 

Здесь «яблоко» встречается дважды , которые могут быть решены с помощью так называемым отслеживанием источников глаголов управления , которые поддерживаются в новом 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) был вызван.