Регулярные выражения (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) был вызван.

Синтаксис

Параметры

Примечания