Как использовать группы захвата (regex capture groups) в Python

PythonBeginner
Практиковаться сейчас

Введение

Группы захвата (capture groups) в регулярных выражениях – это мощные инструменты в Python для извлечения и манипулирования текстовыми данными. В этой лабораторной работе вы изучите основные методы использования групп захвата, получив практическое представление о том, как эти продвинутые механизмы сопоставления с образцом (pattern matching) могут упростить сложные задачи синтаксического анализа строк (string parsing) и извлечения данных (data extraction).

Основы групп захвата в регулярных выражениях (Regex Capture Groups)

Группы захвата (capture groups) – это мощная функция в регулярных выражениях (regular expressions), которая позволяет извлекать и группировать определенные части найденного соответствия (matched pattern). В Python они определяются с помощью круглых скобок () внутри шаблона регулярного выражения (regex pattern).

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

Откройте встроенный терминал (integrated terminal) в WebIDE и перейдите в каталог проекта, если вы еще не там.

cd ~/project

Создайте новый файл с именем basic_capture.py с помощью команды touch.

touch basic_capture.py

Откройте basic_capture.py в редакторе WebIDE и добавьте следующий код Python:

import re

text = "Contact email: john.doe@example.com"
pattern = r"(\w+)\.(\w+)@(\w+)\.(\w+)"

match = re.search(pattern, text)
if match:
    username = match.group(1)
    lastname = match.group(2)
    domain = match.group(3)
    tld = match.group(4)

    print(f"Username: {username}")
    print(f"Lastname: {lastname}")
    print(f"Domain: {domain}")
    print(f"TLD: {tld}")
else:
    print("No match found.")

Сохраните файл.

Теперь запустите скрипт с помощью команды python.

python basic_capture.py

Вы должны увидеть следующий вывод:

Username: john
Lastname: doe
Domain: example
TLD: com

Этот вывод показывает, что скрипт успешно извлек различные части адреса электронной почты, используя группы захвата.

Вы также можете получить доступ ко всем захваченным группам в виде кортежа (tuple), используя метод groups(). Измените файл basic_capture.py, чтобы включить следующие строки после блока if match::

    all_groups = match.groups()
    print(f"All groups: {all_groups}")

Сохраните файл и снова запустите скрипт.

python basic_capture.py

Теперь вывод будет включать кортеж всех захваченных групп:

Username: john
Lastname: doe
Domain: example
TLD: com
All groups: ('john', 'doe', 'example', 'com')

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

Именованные группы захвата (Named Capture Groups)

Именованные группы захвата (named capture groups) предоставляют более читаемый способ доступа к захваченным данным, присваивая имя каждой группе. Синтаксис для именованной группы захвата: (?P<name>...).

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

Создайте новый файл с именем named_capture.py в каталоге ~/project.

touch ~/project/named_capture.py

Откройте named_capture.py в редакторе WebIDE и добавьте следующий код Python:

import re

text = "Product: Laptop, Price: $999.99"
pattern = r"Product: (?P<product>\w+), Price: \$(?P<price>\d+\.\d+)"

match = re.search(pattern, text)
if match:
    product = match.group('product')
    price = match.group('price')
    print(f"Product: {product}, Price: ${price}")
else:
    print("No match found.")

Сохраните файл.

Запустите скрипт с помощью команды python.

python ~/project/named_capture.py

Вы должны увидеть следующий вывод:

Product: Laptop, Price: $999.99

Этот вывод показывает, что скрипт успешно извлек название продукта и цену, используя именованные группы захвата. Вы можете получить доступ к захваченным данным, используя имя группы в качестве ключа в методе group().

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

Практическое использование групп захвата (Practical Capture Group Usage)

Группы захвата (capture groups) широко используются для извлечения данных из различных текстовых форматов, таких как файлы журналов (log files), URL-адреса и структурированные данные.

Давайте создадим скрипт для разбора записи журнала (log entry) с использованием групп захвата.

Создайте новый файл с именем log_parser.py в каталоге ~/project.

touch ~/project/log_parser.py

Откройте log_parser.py в редакторе WebIDE и добавьте следующий код Python:

import re

log_entry = '2023-06-15 14:30:45 [ERROR] Database connection failed'
pattern = r'(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)'

match = re.match(pattern, log_entry)
if match:
    date = match.group(1)
    time = match.group(2)
    log_level = match.group(3)
    message = match.group(4)

    print(f"Date: {date}")
    print(f"Time: {time}")
    print(f"Level: {log_level}")
    print(f"Message: {message}")
else:
    print("No match found.")

Сохраните файл.

Запустите скрипт с помощью команды python.

python ~/project/log_parser.py

Вы должны увидеть следующий вывод:

Date: 2023-06-15
Time: 14:30:45
Level: ERROR
Message: Database connection failed

Этот скрипт успешно разобрал запись журнала и извлек дату, время, уровень журнала (log level) и сообщение, используя группы захвата.

Другой распространенный вариант использования — извлечение информации из URL-адресов. Создайте новый файл с именем url_parser.py в каталоге ~/project.

touch ~/project/url_parser.py

Откройте url_parser.py и добавьте следующий код:

import re

def parse_url(url):
    pattern = r'(https?://)?([^/]+)(/.*)?'
    match = re.match(pattern, url)

    if match:
        protocol = match.group(1) or 'http://'
        domain = match.group(2)
        path = match.group(3) or '/'

        return {
            'protocol': protocol,
            'domain': domain,
            'path': path
        }
    return None

## Example usage
url = 'https://www.example.com/path/to/page'
parsed_url = parse_url(url)
if parsed_url:
    print(f"Protocol: {parsed_url['protocol']}")
    print(f"Domain: {parsed_url['domain']}")
    print(f"Path: {parsed_url['path']}")
else:
    print("Invalid URL format.")

url_no_protocol = 'example.org/another/path'
parsed_url_no_protocol = parse_url(url_no_protocol)
if parsed_url_no_protocol:
    print(f"\nProtocol: {parsed_url_no_protocol['protocol']}")
    print(f"Domain: {parsed_url_no_protocol['domain']}")
    print(f"Path: {parsed_url_no_protocol['path']}")
else:
    print("\nInvalid URL format.")

Сохраните файл.

Запустите скрипт.

python ~/project/url_parser.py

Вывод покажет разобранные компоненты URL-адресов:

Protocol: https://
Domain: www.example.com
Path: /path/to/page

Protocol: http://
Domain: example.org
Path: /another/path

Эти примеры демонстрируют практическое применение групп захвата для извлечения структурированных данных из текста.

Продвинутые методы использования групп захвата (Advanced Capture Group Techniques)

Помимо основных групп захвата (basic capture groups), Python regex предлагает более продвинутые функции, такие как вложенные группы захвата (nested capture groups), не захватывающие группы (non-capturing groups) и просмотры (lookarounds).

Вложенные группы захвата (Nested Capture Groups)

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

Создайте новый файл с именем nested_capture.py в каталоге ~/project.

touch ~/project/nested_capture.py

Откройте nested_capture.py и добавьте следующий код:

import re

def parse_complex_data(text):
    pattern = r'((\w+)\s(\w+))\s\[(\d+)\]'
    match = re.match(pattern, text)

    if match:
        full_name = match.group(1)
        first_name = match.group(2)
        last_name = match.group(3)
        id_number = match.group(4)

        return {
            'full_name': full_name,
            'first_name': first_name,
            'last_name': last_name,
            'id': id_number
        }
    return None

text = 'John Doe [12345]'
result = parse_complex_data(text)
if result:
    print(f"Full Name: {result['full_name']}")
    print(f"First Name: {result['first_name']}")
    print(f"Last Name: {result['last_name']}")
    print(f"ID: {result['id']}")
else:
    print("No match found.")

Сохраните файл.

Запустите скрипт.

python ~/project/nested_capture.py

Вывод покажет извлеченные данные, включая полное имя и его компоненты:

Full Name: John Doe
First Name: John
Last Name: Doe
ID: 12345

Здесь ((\w+)\s(\w+)) — это вложенная группа захвата. group(1) захватывает все "John Doe", group(2) захватывает "John", а group(3) захватывает "Doe". group(4) захватывает ID.

Не захватывающие группы (Non-Capturing Groups)

Иногда вам нужно сгруппировать части шаблона (pattern) для применения квантификаторов (quantifiers) или альтернатив, но вам не нужно захватывать содержимое. Не захватывающие группы (?:...) используются для этой цели.

Создайте новый файл с именем non_capturing.py в каталоге ~/project.

touch ~/project/non_capturing.py

Откройте non_capturing.py и добавьте следующий код:

import re

def extract_domain_info(url):
    ## (?:) creates a non-capturing group
    pattern = r'https?://(?:www\.)?([^/]+)'
    match = re.match(pattern, url)

    if match:
        domain = match.group(1) ## Only the domain is captured
        return domain
    return None

url1 = 'https://www.example.com/path'
domain1 = extract_domain_info(url1)
print(f"Domain from '{url1}': {domain1}")

url2 = 'http://example.org/another/path'
domain2 = extract_domain_info(url2)
print(f"Domain from '{url2}': {domain2}")

Сохраните файл.

Запустите скрипт.

python ~/project/non_capturing.py

Вывод покажет извлеченные доменные имена:

Domain from 'https://www.example.com/path': example.com
Domain from 'http://example.org/another/path': example.org

В этом примере (?:www\.)? соответствует "www.", если он существует, но не захватывает его, поэтому group(1) напрямую захватывает доменное имя.

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

Итог (Summary)

В этой лабораторной работе вы узнали, как использовать группы захвата (regex capture groups) в Python. Вы начали с основных групп захвата (basic capture groups), затем изучили именованные группы захвата (named capture groups) для улучшения читаемости. Вы также попрактиковались в использовании групп захвата для практических задач извлечения данных, таких как разбор файлов журналов (log files) и URL-адресов. Наконец, вы были ознакомлены с продвинутыми методами, такими как вложенные (nested) и не захватывающие группы (non-capturing groups). Освоив эти концепции, вы сможете эффективно извлекать и манипулировать определенными частями текстовых данных, используя регулярные выражения (regular expressions) в Python.