Python正则表达式全面指南:re标准库详解与实践

正则表达式是处理字符串的强大工具,Python通过内置的re模块提供了完整的正则表达式功能。本文将全面介绍正则表达式的基础语法、re模块的核心函数以及实际应用场景。

一、正则表达式基础语法

1.1 基本元字符

正则表达式由普通字符和元字符组成,元字符具有特殊含义:

元字符功能说明示例
.匹配任意单个字符(除换行符)a.c 匹配 "abc"、"a c"
^匹配字符串开头^Hello 匹配以"Hello"开头的字符串
$匹配字符串结尾world$ 匹配以"world"结尾的字符串
*匹配前一个字符0次或多次ab*c 匹配 "ac"、"abc"、"abbc"
+匹配前一个字符1次或多次ab+c 匹配 "abc"、"abbc"
?匹配前一个字符0次或1次ab?c 匹配 "ac"、"abc"
{m,n}匹配前一个字符m到n次a{2,4} 匹配 "aa"、"aaa"、"aaaa"

1.2 字符集与预定义字符类

import re

# 字符集示例
text = "cat bat hat rat"
pattern1 = r'[bc]at'  # 匹配"bat"或"cat"
matches1 = re.findall(pattern1, text)
print(f"字符集匹配: {matches1}")  # 输出: ['cat', 'bat']

# 预定义字符类
text2 = "价格: $100, 重量: 5kg, 日期: 2024-01-01"
pattern2 = r'\d+'  # 匹配一个或多个数字
matches2 = re.findall(pattern2, text2)
print(f"数字匹配: {matches2}")  # 输出: ['100', '5', '2024', '01', '01']

常用预定义字符类:

字符类等价表示说明
\d[0-9]数字字符
\D[^0-9]非数字字符
\w[a-zA-Z0-9_]单词字符
\W[^a-zA-Z0-9_]非单词字符
\s`[ \t 
\r\f\v]`空白字符 
\S`[^ \t 
\r\f\v]`非空白字符 

二、re模块核心函数详解

2.1 匹配函数对比

函数功能返回值适用场景
re.match()从字符串起始位置匹配Match对象或None验证字符串格式
re.search()扫描整个字符串查找匹配Match对象或None查找第一个匹配项
re.findall()查找所有匹配项列表提取所有符合条件的内容
re.finditer()查找所有匹配项迭代器处理大量匹配时节省内存
re.sub()替换匹配项替换后的字符串字符串清洗和格式化
re.split()根据模式分割字符串列表复杂字符串分割

2.2 re.match() - 起始位置匹配

re.match()从字符串的起始位置开始匹配,如果起始位置不符合模式,则返回None。

import re

# re.match() 示例
def test_match():
    patterns = [
        r'hello',
        r'\d+',
        r'^Python'
    ]
    
    test_strings = [
        "hello world",
        "123abc",
        "Python is great",
        "Hi Python"
    ]
    
    for test_str in test_strings:
        print(f"
测试字符串: '{test_str}'")
        for pattern in patterns:
            match = re.match(pattern, test_str)
            if match:
                print(f"  模式 '{pattern}' 匹配成功: {match.group()}")
            else:
                print(f"  模式 '{pattern}' 匹配失败")

test_match()

2.3 re.search() - 全局搜索匹配

re.search()扫描整个字符串,返回第一个匹配的对象。

import re

# re.search() 示例
def test_search():
    text = "我的电话是138-1234-5678,另一个电话是139-8765-4321"
    
    # 搜索电话号码
    phone_pattern = r'\d{3}-\d{4}-\d{4}'
    match = re.search(phone_pattern, text)
    
    if match:
        print(f"找到电话号码: {match.group()}")  # 输出: 找到电话号码: 138-1234-5678
        print(f"匹配位置: {match.start()} - {match.end()}")  # 输出: 匹配位置: 5 - 18
        print(f"匹配范围: {match.span()}")  # 输出: 匹配范围: (5, 18)
    else:
        print("未找到电话号码")

test_search()

2.4 re.findall() - 查找所有匹配

re.findall()返回字符串中所有非重叠匹配的列表。

import re

# re.findall() 示例
def test_findall():
    # 示例1: 提取所有电子邮件
    text1 = "联系我们: admin@example.com, sales@company.org, support@test.cn"
    email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
    emails = re.findall(email_pattern, text1)
    print(f"找到的邮箱: {emails}")
    
    # 示例2: 提取HTML标签中的内容
    html_text = "<h1>标题</h1><p>段落内容</p><div>容器</div>"
    tag_content = re.findall(r'<[^>]+>([^<]+)</[^>]+>', html_text)
    print(f"标签内容: {tag_content}")
    
    # 示例3: 提取价格信息
    price_text = "商品A: ¥100.50, 商品B: $200.75, 商品C: €150.00"
    prices = re.findall(r'[¥$€](\d+\.\d+)', price_text)
    print(f"价格: {prices}")

test_findall()

2.5 re.finditer() - 返回匹配迭代器

re.finditer()返回一个迭代器,逐个产生Match对象,适合处理大量数据。

import re

# re.finditer() 示例
def test_finditer():
    text = """
    用户信息:
    姓名: 张三, 年龄: 25, 城市: 北京
    姓名: 李四, 年龄: 30, 城市: 上海
    姓名: 王五, 年龄: 28, 城市: 广州
    """
    
    # 使用命名分组提取信息
    pattern = r'姓名:\s*(?P<name>\w+),\s*年龄:\s*(?P<age>\d+),\s*城市:\s*(?P<city>\w+)'
    
    print("用户信息提取:")
    for match in re.finditer(pattern, text):
        name = match.group('name')
        age = match.group('age')
        city = match.group('city')
        print(f"  {name}, {age}岁, 来自{city}")

test_finditer()

2.6 re.sub() - 替换匹配内容

re.sub()用于替换字符串中的匹配项。

import re

# re.sub() 示例
def test_sub():
    # 示例1: 敏感信息脱敏
    text1 = "银行卡号: 6225-3801-2345-6789, 身份证: 110101199001011234"
    
    # 银行卡号脱敏
    desensitized_bank = re.sub(r'\d{4}-\d{4}-\d{4}-', '****-****-****-', text1)
    # 身份证号脱敏
    desensitized_id = re.sub(r'(\d{6})\d{8}(\d{4})', r'\1********\2', desensitized_bank)
    
    print(f"脱敏后: {desensitized_id}")
    
    # 示例2: 日期格式转换
    date_text = "今天是2024/01/15,会议在2024-02-20"
    standardized_date = re.sub(r'(\d{4})[/-](\d{2})[/-](\d{2})', r'\1年\2月\3日', date_text)
    print(f"日期标准化: {standardized_date}")
    
    # 示例3: 使用函数进行复杂替换
    def multiply_numbers(match):
        number = int(match.group())
        return str(number * 2)
    
    math_text = "数字: 10, 20, 30"
    doubled_text = re.sub(r'\d+', multiply_numbers, math_text)
    print(f"数字加倍: {doubled_text}")

test_sub()

2.7 re.split() - 模式分割字符串

re.split()使用正则表达式模式分割字符串。

import re

# re.split() 示例
def test_split():
    # 示例1: 复杂分隔符分割
    text1 = "苹果,香蕉;橙子|葡萄 西瓜"
    fruits = re.split(r'[,;|\s]+', text1)
    print(f"水果列表: {fruits}")
    
    # 示例2: 保留分隔符
    text2 = "100+200-300*400/500"
    # 使用捕获分组保留运算符
    parts = re.split(r'([+*-/])', text2)
    print(f"分割结果: {parts}")
    
    # 示例3: 多空格分割
    text3 = "这    是    一个    有很多    空格的    文本"
    words = re.split(r'\s+', text3)
    print(f"单词列表: {words}")

test_split()

三、高级功能与实战应用

3.1 分组与捕获

分组是正则表达式的重要特性,可以提取子匹配内容。

import re

# 分组捕获示例
def group_demo():
    # 提取日志信息
    log_entry = "2024-01-15 14:30:25 [INFO] User login successful (user_id: 12345)"
    
    # 使用命名分组
    log_pattern = r'(?P<date>\d{4}-\d{2}-\d{2})\s+(?P<time>\d{2}:\d{2}:\d{2})\s+\[(?P<level>\w+)\]\s+(?P<message>.*?)\s+\(user_id:\s*(?P<user_id>\d+)\)'
    
    match = re.search(log_pattern, log_entry)
    if match:
        print("日志解析结果:")
        print(f"  日期: {match.group('date')}")
        print(f"  时间: {match.group('time')}")
        print(f"  级别: {match.group('level')}")
        print(f"  消息: {match.group('message')}")
        print(f"  用户ID: {match.group('user_id')}")
    
    # 非捕获分组
    text = "color colour"
    # 使用非捕获分组 (?:)
    pattern = r'col(?:ou)?r'
    matches = re.findall(pattern, text)
    print(f"颜色匹配: {matches}")

group_demo()

3.2 贪婪匹配与非贪婪匹配

正则表达式默认使用贪婪匹配,可以通过?改为非贪婪匹配。

import re

# 贪婪vs非贪婪匹配
def greedy_vs_lazy():
    html_text = "<div>内容1</div><div>内容2</div><div>内容3</div>"
    
    # 贪婪匹配 - 匹配最长的可能
    greedy_pattern = r'<div>.*</div>'
    greedy_match = re.search(greedy_pattern, html_text)
    print(f"贪婪匹配: {greedy_match.group() if greedy_match else '无匹配'}")
    
    # 非贪婪匹配 - 匹配最短的可能
    lazy_pattern = r'<div>.*?</div>'
    lazy_matches = re.findall(lazy_pattern, html_text)
    print(f"非贪婪匹配: {lazy_matches}")

greedy_vs_lazy()

3.3 标志参数的使用

re模块支持多种标志参数来改变匹配行为。

import re

# 标志参数示例
def flags_demo():
    text = "Hello
World
Python"
    
    # 多行模式 - 让^和$匹配每行的开头和结尾
    multiline_matches = re.findall(r'^\w+', text, re.MULTILINE)
    print(f"多行模式匹配: {multiline_matches}")
    
    # 忽略大小写
    case_insensitive = re.findall(r'hello', text, re.IGNORECASE)
    print(f"忽略大小写匹配: {case_insensitive}")
    
    # 点号匹配所有字符(包括换行符)
    dotall_match = re.search(r'Hello.*Python', text, re.DOTALL)
    print(f"点号匹配所有: {dotall_match.group() if dotall_match else '无匹配'}")

flags_demo()

3.4 编译正则表达式

对于需要重复使用的模式,可以先编译再使用,提高效率。

import re
import time

# 编译正则表达式示例
def compile_demo():
    # 准备测试数据
    emails = [f"user{i}@example.com" for i in range(1000)]
    
    # 未编译的方式
    start_time = time.time()
    pattern = r'^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
    valid_emails = []
    for email in emails:
        if re.match(pattern, email):
            valid_emails.append(email)
    uncompiled_time = time.time() - start_time
    
    # 编译后的方式
    start_time = time.time()
    compiled_pattern = re.compile(pattern)
    valid_emails_compiled = []
    for email in emails:
        if compiled_pattern.match(email):
            valid_emails_compiled.append(email)
    compiled_time = time.time() - start_time
    
    print(f"未编译方式耗时: {uncompiled_time:.4f}秒")
    print(f"编译后方式耗时: {compiled_time:.4f}秒")
    print(f"性能提升: {(uncompiled_time - compiled_time)/uncompiled_time*100:.1f}%")

compile_demo()

四、实战应用案例

4.1 数据验证

import re

# 数据验证函数
def data_validation():
    def validate_email(email):
        pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
        return bool(re.match(pattern, email))
    
    def validate_phone(phone):
        pattern = r'^1[3-9]\d{9}$'  # 中国手机号
        return bool(re.match(pattern, phone))
    
    def validate_id_card(id_card):
        pattern = r'^\d{17}[\dXx]$'  # 简化版身份证验证
        return bool(re.match(pattern, id_card))
    
    # 测试验证
    test_data = [
        "test@example.com",
        "13812345678", 
        "110101199001011234",
        "invalid-email",
        "12345678901",
        "ABC12345678901234"
    ]
    
    print("数据验证结果:")
    for data in test_data:
        email_valid = validate_email(data)
        phone_valid = validate_phone(data)
        id_valid = validate_id_card(data)
        
        print(f"  '{data}': 邮箱({email_valid}), 手机({phone_valid}), 身份证({id_valid})")

data_validation()

4.2 文本数据提取

import re

# 文本数据提取
def text_extraction():
    # 复杂的文本数据
    complex_text = """
    订单信息:
    订单号: ORD20240115001, 金额: ¥1,250.50, 状态: 已完成
    客户: 张三, 电话: 138-1234-5678, 邮箱: zhangsan@email.com
    地址: 北京市朝阳区xxx街道100号
    
    商品清单:
    1. 笔记本电脑 - 数量: 1 - 单价: ¥8,999.00
    2. 鼠标 - 数量: 2 - 单价: ¥150.00
    3. 键盘 - 数量: 1 - 单价: ¥450.00
    """
    
    # 提取订单基本信息
    order_info = {}
    
    # 提取订单号
    order_match = re.search(r'订单号:\s*(\w+)', complex_text)
    if order_match:
        order_info['order_no'] = order_match.group(1)
    
    # 提取金额(处理千分位分隔符)
    amount_match = re.search(r'金额:\s*¥([\d,]+\.\d+)', complex_text)
    if amount_match:
        amount = amount_match.group(1).replace(',', '')
        order_info['amount'] = float(amount)
    
    # 提取客户信息
    customer_match = re.search(r'客户:\s*(\w+)', complex_text)
    if customer_match:
        order_info['customer'] = customer_match.group(1)
    
    # 提取商品信息
    products = []
    product_pattern = r'(\d+)\.\s*([^-]+)\s*-\s*数量:\s*(\d+)\s*-\s*单价:\s*¥([\d,]+\.\d+)'
    
    for match in re.finditer(product_pattern, complex_text):
        product = {
            'seq': match.group(1),
            'name': match.group(2).strip(),
            'quantity': int(match.group(3)),
            'price': float(match.group(4).replace(',', ''))
        }
        products.append(product)
    
    order_info['products'] = products
    
    print("提取的订单信息:")
    for key, value in order_info.items():
        if key != 'products':
            print(f"  {key}: {value}")
        else:
            print("  商品清单:")
            for product in value:
                print(f"    {product['seq']}. {product['name']} - {product['quantity']}件 - ¥{product['price']}")

text_extraction()

4.3 日志分析

import re
from collections import Counter

# 日志分析
def log_analysis():
    # 模拟日志数据
    logs = """
    2024-01-15 08:30:15 [INFO] User login successful: user123
    2024-01-15 08:35:22 [ERROR] Database connection failed
    2024-01-15 08:40:10 [WARN] High memory usage detected
    2024-01-15 09:15:30 [INFO] File upload completed: file.pdf
    2024-01-15 09:20:45 [ERROR] Permission denied for user: user456
    2024-01-15 10:05:18 [INFO] User logout: user123
    2024-01-15 10:30:55 [ERROR] Invalid API request
    """
    
    # 分析日志级别分布
    level_pattern = r'\[(\w+)\]'
    levels = re.findall(level_pattern, logs)
    level_count = Counter(levels)
    
    print("日志级别统计:")
    for level, count in level_count.items():
        print(f"  {level}: {count}次")
    
    # 提取错误日志的详细信息
    error_pattern = r'\[ERROR\]\s*(.+)'
    errors = re.findall(error_pattern, logs)
    
    print("
错误日志详情:")
    for i, error in enumerate(errors, 1):
        print(f"  {i}. {error}")
    
    # 提取用户操作
    user_actions = re.findall(r'User\s+(\w+):\s*(\w+)', logs)
    print("
用户操作记录:")
    for action, user in user_actions:
        print(f"  用户 {user} 执行了 {action} 操作")

log_analysis()

五、性能优化与最佳实践

5.1 性能优化技巧

import re
import time

def performance_tips():
    # 1. 使用原始字符串
    good_pattern = r'\d+\.\d+'  # 推荐
    bad_pattern = '\\d+\\.\\d+'  # 不推荐
    
    # 2. 避免回溯灾难
    text = "aaaaaaaaaaaaaaaaaaaaab"
    
    # 糟糕的模式 -  catastrophic backtracking
    start_time = time.time()
    try:
        bad_match = re.search(r'(a+)+b', text)
        bad_time = time.time() - start_time
    except:
        bad_time = float('inf')
    
    # 优化的模式
    start_time = time.time()
    good_match = re.search(r'a+b', text)
    good_time = time.time() - start_time
    
    print(f"糟糕模式耗时: {bad_time:.6f}秒")
    print(f"优化模式耗时: {good_time:.6f}秒")
    
    # 3. 使用适当的量词
    text2 = "<div>content</div>" * 1000
    
    # 贪婪量词
    start_time = time.time()
    greedy_result = re.findall(r'<div>.*</div>', text2)
    greedy_time = time.time() - start_time
    
    # 非贪婪量词
    start_time = time.time()
    lazy_result = re.findall(r'<div>.*?</div>', text2)
    lazy_time = time.time() - start_time
    
    print(f"贪婪量词匹配{len(greedy_result)}次,耗时: {greedy_time:.6f}秒")
    print(f"非贪婪量词匹配{len(lazy_result)}次,耗时: {lazy_time:.6f}秒")

performance_tips()

5.2 错误处理与调试

import re

def error_handling():
    def safe_re_search(pattern, text, flags=0):
        try:
            compiled = re.compile(pattern, flags)
            return compiled.search(text)
        except re.error as e:
            print(f"正则表达式错误: {e}")
            return None
    
    # 测试错误处理
    test_cases = [
        (r'valid_pattern\d+', 'text123'),
        (r'invalid[pattern', 'text'),  # 未闭合字符集
        (r'*invalid', 'text'),         # 量词使用错误
    ]
    
    for pattern, text in test_cases:
        print(f"
测试模式: {pattern}")
        result = safe_re_search(pattern, text)
        if result:
            print(f"  匹配成功: {result.group()}")
        else:
            print("  匹配失败或模式错误")

error_handling()

正则表达式是Python文本处理的利器,通过熟练掌握re模块的各种函数和正则表达式语法,可以高效解决复杂的字符串处理问题。在实际应用中,建议根据具体需求选择合适的函数,注意性能优化,并妥善处理可能的异常情况。


参考来源

 

更多推荐