如何在 Python 中使用正则表达式捕获组

PythonBeginner
立即练习

介绍

正则表达式捕获组(regular expression capture groups)是 Python 中用于提取和操作文本数据的强大工具。在这个实验(lab)中,你将学习使用捕获组的基本技术,深入了解这些高级模式匹配机制如何简化复杂的字符串解析和数据提取任务。

正则表达式捕获组基础

捕获组(capture groups)是正则表达式中一个强大的特性,允许你提取和分组匹配模式中的特定部分。在 Python 中,它们通过在正则表达式模式中使用括号 () 来定义。

让我们从创建一个 Python 脚本来演示基本的捕获组用法开始。

打开 WebIDE 中的集成终端,如果尚未进入项目目录,请导航到该目录。

cd ~/project

使用 touch 命令创建一个名为 basic_capture.py 的新文件。

touch basic_capture.py

在 WebIDE 编辑器中打开 basic_capture.py,并添加以下 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

此输出表明该脚本已成功使用捕获组提取了电子邮件地址的不同部分。

你还可以使用 groups() 方法将所有捕获的组作为元组(tuple)访问。修改 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)通过为每个组分配一个名称,提供了一种更易读的方式来访问捕获的数据。命名捕获组的语法是 (?P<name>...)

让我们创建一个新的 Python 脚本来演示命名捕获组。

~/project 目录中创建一个名为 named_capture.py 的新文件。

touch ~/project/named_capture.py

在 WebIDE 编辑器中打开 named_capture.py,并添加以下 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() 方法中的键来访问捕获的数据。

命名捕获组使你的正则表达式模式和后续代码更易于理解,尤其是在处理具有许多捕获组的复杂模式时。

捕获组的实际应用

捕获组被广泛用于从各种文本格式(如日志文件、URL 和结构化数据)中提取数据。

让我们创建一个脚本来使用捕获组解析日志条目。

~/project 目录中创建一个名为 log_parser.py 的新文件。

touch ~/project/log_parser.py

在 WebIDE 编辑器中打开 log_parser.py,并添加以下 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

此脚本成功解析了日志条目,并使用捕获组提取了日期、时间、日志级别和消息。

另一个常见的用例是从 URL 中提取信息。在 ~/project 目录中创建一个名为 url_parser.py 的新文件。

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

这些示例演示了捕获组在从文本中提取结构化数据方面的实际应用。

高级捕获组技巧

除了基本的捕获组之外,Python 正则表达式还提供更高级的功能,如嵌套捕获组、非捕获组和环视(lookarounds)。

嵌套捕获组

捕获组可以嵌套在其他捕获组中,以提取更细粒度的信息。

~/project 目录中创建一个名为 nested_capture.py 的新文件。

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。

非捕获组

有时你需要对模式的部分进行分组,以便应用量词或选择,但你不需要捕获内容。非捕获组 (?:...) 用于此目的。

~/project 目录中创建一个名为 non_capturing.py 的新文件。

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) 直接捕获域名。

当你只需要捕获较大模式的特定部分时,使用非捕获组可以稍微提高性能,并使捕获的组索引更清晰。

总结

在这个实验中,你已经学习了如何在 Python 中使用正则表达式(regex)捕获组。你从基本的捕获组开始,然后探索了命名捕获组以提高可读性。你还练习了使用捕获组来完成实际的数据提取任务,例如解析日志文件和 URL。最后,你被介绍了嵌套组和非捕获组等高级技巧。通过掌握这些概念,你可以使用 Python 中的正则表达式有效地提取和操作文本数据的特定部分。