[Python教程系列-10] 正则表达式基础:文本处理的强大工具v

108 阅读12分钟

引言

在日常的编程工作中,我们经常需要处理文本数据,比如验证用户输入、提取特定信息、格式化文本等。正则表达式(Regular Expression,简称regex或regexp)是一种强大的文本处理工具,它使用特定的语法来描述文本模式,可以高效地完成复杂的字符串匹配、查找、替换等操作。

正则表达式几乎在所有的编程语言中都有实现,Python通过re模块提供了完整的正则表达式支持。掌握正则表达式不仅能够大大提高文本处理的效率,还能让我们写出更加简洁和优雅的代码。

在前面的章节中,我们学习了Python的基础语法、数据结构、面向对象编程、文件操作、标准库使用以及环境管理等内容。本章将深入学习正则表达式的基础知识和在Python中的应用,帮助你掌握这一重要的文本处理工具。

学习目标

完成本章学习后,你将能够:

  • 理解正则表达式的基本概念和工作原理
  • 掌握常用的正则表达式元字符和语法
  • 使用Python的re模块进行模式匹配和文本处理
  • 编写复杂的正则表达式来解决实际问题
  • 理解贪婪匹配和非贪婪匹配的区别
  • 掌握正则表达式的编译和优化技巧
  • 应用正则表达式解决常见的文本处理任务

核心知识点讲解

1. 正则表达式概述

正则表达式是一种用来描述字符串模式的形式化语言。它由普通字符(如字母、数字)和特殊字符(称为元字符)组成,用于匹配、查找和替换符合特定模式的文本。

正则表达式的特点:

  • 简洁性:用简短的表达式描述复杂的文本模式
  • 灵活性:可以匹配各种复杂的文本结构
  • 高效性:现代正则表达式引擎经过优化,匹配速度快
  • 通用性:几乎所有编程语言都支持正则表达式

2. 基本元字符

元字符是正则表达式中具有特殊含义的字符,它们构成了正则表达式的核心语法。

常用元字符:

  • . : 匹配除换行符外的任意字符
  • ^ : 匹配字符串的开始
  • $ : 匹配字符串的结束
  • * : 匹配前面的字符0次或多次
  • + : 匹配前面的字符1次或多次
  • ? : 匹配前面的字符0次或1次
  • {n} : 匹配前面的字符恰好n次
  • {n,} : 匹配前面的字符至少n次
  • {n,m} : 匹配前面的字符至少n次,至多m次
  • [] : 字符类,匹配方括号内的任意字符
  • | : 或操作符,匹配左边或右边的表达式
  • () : 分组,将多个字符组合为一个单元
  • \ : 转义字符,用于匹配特殊字符本身

3. 字符类和预定义字符集

字符类允许我们匹配特定类型的字符,而预定义字符集提供了常用的字符类别快捷方式。

常用字符类:

  • [abc] : 匹配a、b或c中的任意一个字符
  • [^abc] : 匹配除了a、b、c之外的任意字符
  • [a-z] : 匹配任意小写字母
  • [A-Z] : 匹配任意大写字母
  • [0-9] : 匹配任意数字

预定义字符集:

  • \d : 匹配任意数字,等价于[0-9]
  • \D : 匹配任意非数字字符,等价于[^0-9]
  • \w : 匹配任意字母、数字或下划线,等价于[a-zA-Z0-9_]
  • \W : 匹配任意非字母、数字或下划线字符
  • \s : 匹配任意空白字符(空格、制表符、换行符等)
  • \S : 匹配任意非空白字符

4. 量词和贪婪匹配

量词用于指定前面字符或组的重复次数,理解贪婪匹配和非贪婪匹配的区别非常重要。

量词类型:

  • 贪婪量词:尽可能多地匹配字符

    • * : 0次或多次
    • + : 1次或多次
    • ? : 0次或1次
    • {n,m} : n到m次
  • 非贪婪量词:尽可能少地匹配字符

    • *? : 0次或多次(非贪婪)
    • +? : 1次或多次(非贪婪)
    • ?? : 0次或1次(非贪婪)
    • {n,m}? : n到m次(非贪婪)

5. 分组和捕获

分组允许我们将多个字符组合为一个单元,并可以捕获匹配的内容用于后续处理。

分组类型:

  • 捕获分组:(...) - 匹配内容会被保存
  • 非捕获分组:(?:...) - 匹配但不保存内容
  • 命名分组:(?P<name>...) - 带名称的捕获分组
  • 反向引用:\1, \2 等 - 引用前面捕获的分组

6. Python re模块

Python的re模块提供了完整的正则表达式支持,包含丰富的函数和方法。

主要函数:

  • re.match() : 从字符串开头匹配模式
  • re.search() : 在整个字符串中搜索模式
  • re.findall() : 查找所有匹配的子串
  • re.finditer() : 返回匹配对象的迭代器
  • re.sub() : 替换匹配的子串
  • re.split() : 根据模式分割字符串
  • re.compile() : 编译正则表达式对象

7. 匹配对象和标志

匹配对象包含了匹配结果的详细信息,而标志可以修改正则表达式的匹配行为。

匹配对象属性:

  • .group() : 返回匹配的字符串
  • .groups() : 返回所有捕获分组的元组
  • .start() : 返回匹配开始的位置
  • .end() : 返回匹配结束的位置
  • .span() : 返回匹配的起始和结束位置

常用标志:

  • re.IGNORECASEre.I : 忽略大小写
  • re.MULTILINEre.M : 多行模式
  • re.DOTALLre.S : 使.匹配包括换行符在内的所有字符
  • re.VERBOSEre.X : 详细模式,允许添加注释和空白

代码示例与实战

实战1:基础正则表达式练习

import re

# 1. 基本匹配
text = "Hello, my email is example@test.com and phone is 138-1234-5678"
print("原文本:", text)

# 匹配邮箱地址
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, text)
print("找到的邮箱:", emails)

# 匹配手机号码
phone_pattern = r'1[3-9]\d{9}'
phones = re.findall(phone_pattern, text)
print("找到的手机号:", phones)

# 2. 使用分组提取信息
phone_with_dash_pattern = r'(1[3-9]\d)-(\d{4})-(\d{4})'
match = re.search(phone_with_dash_pattern, text)
if match:
    print("完整号码:", match.group(0))
    print("区号:", match.group(1))
    print("前四位:", match.group(2))
    print("后四位:", match.group(3))

# 3. 命名分组
named_pattern = r'(?P<area>1[3-9]\d)-(?P<prefix>\d{4})-(?P<suffix>\d{4})'
match = re.search(named_pattern, text)
if match:
    print("命名分组结果:")
    print("区号:", match.group('area'))
    print("前缀:", match.group('prefix'))
    print("后缀:", match.group('suffix'))

实战2:文本验证和清理工具

import re

class TextValidator:
    """文本验证工具类"""
    
    # 预定义的正则表达式模式
    PATTERNS = {
        'email': r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
        'phone': r'^1[3-9]\d{9}$',
        'chinese_phone': r'^(\+86)?1[3-9]\d{9}$',
        'id_card': r'^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$',
        'url': r'^https?://(?:[-\w.])+(?:\:[0-9]+)?(?:/(?:[\w/_.])*(?:\?(?:[\w&=%.])*)?(?:\#(?:[\w.])*)?)?$',
        'ipv4': r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
        'strong_password': r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$'
    }
    
    @classmethod
    def validate(cls, pattern_name, text):
        """验证文本是否符合指定模式"""
        if pattern_name not in cls.PATTERNS:
            raise ValueError(f"未知的模式名称: {pattern_name}")
        
        pattern = cls.PATTERNS[pattern_name]
        return bool(re.match(pattern, text))
    
    @classmethod
    def find_all(cls, pattern_name, text):
        """查找文本中所有符合指定模式的内容"""
        if pattern_name not in cls.PATTERNS:
            raise ValueError(f"未知的模式名称: {pattern_name}")
        
        pattern = cls.PATTERNS[pattern_name]
        return re.findall(pattern, text)

class TextCleaner:
    """文本清理工具类"""
    
    @staticmethod
    def remove_extra_spaces(text):
        """移除多余的空格"""
        # 将多个连续空格替换为单个空格
        return re.sub(r'\s+', ' ', text).strip()
    
    @staticmethod
    def remove_html_tags(text):
        """移除HTML标签"""
        return re.sub(r'<[^>]+>', '', text)
    
    @staticmethod
    def extract_chinese(text):
        """提取中文字符"""
        return ''.join(re.findall(r'[\u4e00-\u9fff]+', text))
    
    @staticmethod
    def mask_sensitive_info(text):
        """遮蔽敏感信息"""
        # 遮蔽手机号中间4位
        text = re.sub(r'(1[3-9]\d)(\d{4})(\d{4})', r'\1****\3', text)
        # 遮蔽邮箱@前的部分(保留前3位和后2位)
        text = re.sub(r'([a-zA-Z0-9._%+-]{3})[a-zA-Z0-9._%+-]*(@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})', 
                      r'\1****\2', text)
        return text

# 使用示例
if __name__ == "__main__":
    # 验证功能
    print("=== 文本验证示例 ===")
    test_cases = [
        ('email', 'test@example.com'),
        ('email', 'invalid-email'),
        ('phone', '13812345678'),
        ('phone', '12345678901'),
        ('url', 'https://www.example.com'),
        ('url', 'not-a-url')
    ]
    
    for pattern_name, text in test_cases:
        result = TextValidator.validate(pattern_name, text)
        print(f"{pattern_name}: {text} -> {'有效' if result else '无效'}")
    
    # 清理功能
    print("\n=== 文本清理示例 ===")
    messy_text = """
    这是一个   包含多余空格    的文本。
    <p>这里有一些<strong>HTML标签</strong></p>
    联系电话:13812345678,邮箱:example@test.com
    这段文字包含English和中文混合内容。
    """
    
    print("原文本:")
    print(messy_text)
    
    cleaned = TextCleaner.remove_extra_spaces(messy_text)
    print("\n清理多余空格后:")
    print(cleaned)
    
    no_html = TextCleaner.remove_html_tags(cleaned)
    print("\n移除HTML标签后:")
    print(no_html)
    
    chinese_only = TextCleaner.extract_chinese(no_html)
    print("\n提取中文内容:")
    print(chinese_only)
    
    masked = TextCleaner.mask_sensitive_info(no_html)
    print("\n遮蔽敏感信息后:")
    print(masked)

实战3:日志分析工具

import re
from datetime import datetime
from collections import defaultdict, Counter

class LogAnalyzer:
    """日志分析工具"""
    
    def __init__(self):
        # Apache访问日志的正则表达式模式
        self.apache_pattern = re.compile(
            r'(?P<ip>\d+\.\d+\.\d+\.\d+)\s+'  # IP地址
            r'-\s+-\s+'  # 远程用户标识符和认证用户标识符
            r'$ (?P<datetime>[^\]]+) $\s+'  # 时间戳
            r'"(?P<method>\w+)\s+(?P<url>[^\s]+)\s+(?P<protocol>[^"]+)"\s+'  # 请求行
            r'(?P<status>\d+)\s+'  # 状态码
            r'(?P<size>\d+|-)\s+'  # 响应大小
            r'"(?P<referer>[^"]*)"\s+'  # Referer
            r'"(?P<user_agent>[^"]*)"'  # User-Agent
        )
        
        # Nginx访问日志的正则表达式模式
        self.nginx_pattern = re.compile(
            r'(?P<ip>\d+\.\d+\.\d+\.\d+)\s+'  # IP地址
            r'-\s+-\s+'  # 远程用户标识符和认证用户标识符
            r'$ (?P<datetime>[^\]]+) $\s+'  # 时间戳
            r'"(?P<method>\w+)\s+(?P<url>[^\s]+)\s+(?P<protocol>[^"]+)"\s+'  # 请求行
            r'(?P<status>\d+)\s+'  # 状态码
            r'(?P<size>\d+)\s+'  # 响应大小
            r'"(?P<referer>[^"]*)"\s+'  # Referer
            r'"(?P<user_agent>[^"]*)"'  # User-Agent
        )
    
    def parse_apache_log(self, log_line):
        """解析Apache日志行"""
        match = self.apache_pattern.match(log_line)
        if match:
            return match.groupdict()
        return None
    
    def parse_nginx_log(self, log_line):
        """解析Nginx日志行"""
        match = self.nginx_pattern.match(log_line)
        if match:
            return match.groupdict()
        return None
    
    def analyze_logs(self, log_lines, log_type='apache'):
        """分析日志"""
        stats = {
            'total_requests': 0,
            'status_codes': Counter(),
            'ips': Counter(),
            'urls': Counter(),
            'methods': Counter(),
            'user_agents': Counter(),
            'errors': []  # 存储解析失败的行
        }
        
        parse_func = self.parse_apache_log if log_type == 'apache' else self.parse_nginx_log
        
        for line_num, line in enumerate(log_lines, 1):
            parsed = parse_func(line.strip())
            if parsed:
                stats['total_requests'] += 1
                
                # 统计状态码
                stats['status_codes'][parsed['status']] += 1
                
                # 统计IP地址
                stats['ips'][parsed['ip']] += 1
                
                # 统计URL
                stats['urls'][parsed['url']] += 1
                
                # 统计请求方法
                stats['methods'][parsed['method']] += 1
                
                # 统计User-Agent
                if parsed['user_agent']:
                    stats['user_agents'][parsed['user_agent']] += 1
            else:
                stats['errors'].append(f"第{line_num}行解析失败: {line.strip()}")
        
        return stats
    
    def generate_report(self, stats):
        """生成分析报告"""
        print("=" * 50)
        print("日志分析报告")
        print("=" * 50)
        print(f"总请求数: {stats['total_requests']}")
        
        print("\n状态码统计:")
        for code, count in stats['status_codes'].most_common():
            print(f"  {code}: {count} 次")
        
        print("\n访问最多的IP地址 (Top 10):")
        for ip, count in stats['ips'].most_common(10):
            print(f"  {ip}: {count} 次")
        
        print("\n访问最多的URL (Top 10):")
        for url, count in stats['urls'].most_common(10):
            print(f"  {url}: {count} 次")
        
        print("\n请求方法统计:")
        for method, count in stats['methods'].most_common():
            print(f"  {method}: {count} 次")
        
        print("\n最常见的User-Agent (Top 5):")
        for ua, count in stats['user_agents'].most_common(5):
            print(f"  {ua[:50]}...: {count} 次")
        
        if stats['errors']:
            print(f"\n解析错误 ({len(stats['errors'])} 条):")
            for error in stats['errors'][:5]:  # 只显示前5个错误
                print(f"  {error}")

# 模拟日志数据
sample_logs = [
    '192.168.1.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"',
    '192.168.1.2 - - [25/Dec/2023:10:01:00 +0000] "POST /login HTTP/1.1" 200 567 "-" "Chrome/91.0.4472.124"',
    '192.168.1.3 - - [25/Dec/2023:10:02:00 +0000] "GET /admin.php HTTP/1.1" 404 234 "-" "Mozilla/5.0"',
    '192.168.1.1 - - [25/Dec/2023:10:03:00 +0000] "GET /style.css HTTP/1.1" 200 789 "-" "Safari/14.1.1"',
    '192.168.1.4 - - [25/Dec/2023:10:04:00 +0000] "GET /script.js HTTP/1.1" 200 456 "-" "Firefox/89.0"',
    '192.168.1.5 - - [25/Dec/2023:10:05:00 +0000] "GET /favicon.ico HTTP/1.1" 404 123 "-" "Bot/1.0"'
]

# 使用示例
if __name__ == "__main__":
    analyzer = LogAnalyzer()
    stats = analyzer.analyze_logs(sample_logs, 'apache')
    analyzer.generate_report(stats)

实战4:数据提取和格式化工具

import re
from datetime import datetime

class DataExtractor:
    """数据提取工具"""
    
    def __init__(self):
        # 定义各种数据模式
        self.patterns = {
            'dates': [
                r'\b\d{4}-\d{2}-\d{2}\b',  # YYYY-MM-DD
                r'\b\d{2}/\d{2}/\d{4}\b',  # MM/DD/YYYY
                r'\b\d{4}年\d{1,2}月\d{1,2}日\b'  # YYYY年MM月DD日
            ],
            'times': [
                r'\b\d{2}:\d{2}:\d{2}\b',  # HH:MM:SS
                r'\b\d{1,2}:\d{2}\s*(?:AM|PM)\b'  # H:MM AM/PM
            ],
            'prices': [
                r'\b¥?\d+(?:,\d{3})*(?:\.\d{2})?\b',  # ¥1,234.56
                r'\b\$\d+(?:,\d{3})*(?:\.\d{2})?\b'   # $1,234.56
            ],
            'percentages': [
                r'\b\d+(?:\.\d+)?%\b'  # 12.5%
            ]
        }
    
    def extract_dates(self, text):
        """提取日期"""
        dates = []
        for pattern in self.patterns['dates']:
            matches = re.findall(pattern, text)
            dates.extend(matches)
        return dates
    
    def extract_times(self, text):
        """提取时间"""
        times = []
        for pattern in self.patterns['times']:
            matches = re.findall(pattern, text)
            times.extend(matches)
        return times
    
    def extract_prices(self, text):
        """提取价格"""
        prices = []
        for pattern in self.patterns['prices']:
            matches = re.findall(pattern, text)
            prices.extend(matches)
        return prices
    
    def extract_percentages(self, text):
        """提取百分比"""
        percentages = []
        for pattern in self.patterns['percentages']:
            matches = re.findall(pattern, text)
            percentages.extend(matches)
        return percentages
    
    def extract_structured_data(self, text):
        """提取结构化数据"""
        # 提取姓名和年龄的模式
        name_age_pattern = r'姓名[::]\s*([^\s,,]+).*?年龄[::]\s*(\d+)'
        matches = re.findall(name_age_pattern, text)
        
        people = []
        for name, age in matches:
            people.append({
                'name': name.strip(),
                'age': int(age)
            })
        
        return people

class DataFormatter:
    """数据格式化工具"""
    
    @staticmethod
    def normalize_phone_numbers(text):
        """标准化电话号码格式"""
        # 匹配各种电话号码格式并统一为 XXX-XXXX-XXXX 格式
        def format_phone(match):
            digits = re.sub(r'\D', '', match.group())
            if len(digits) == 11 and digits.startswith('1'):
                return f"{digits[:3]}-{digits[3:7]}-{digits[7:]}"
            return match.group()
        
        phone_pattern = r'(?:\+86[-\s]?)?1[3-9]\d[-\s]?\d{4}[-\s]?\d{4}|\b1[3-9]\d{9}\b'
        return re.sub(phone_pattern, format_phone, text)
    
    @staticmethod
    def format_currency(amount_str):
        """格式化货币金额"""
        # 移除货币符号和逗号,转换为浮点数
        clean_amount = re.sub(r'[¥$,]', '', amount_str)
        try:
            amount = float(clean_amount)
            return f"¥{amount:,.2f}"
        except ValueError:
            return amount_str
    
    @staticmethod
    def standardize_dates(text):
        """标准化日期格式为YYYY-MM-DD"""
        # 处理 YYYY/MM/DD 格式
        text = re.sub(r'\b(\d{4})/(\d{1,2})/(\d{1,2})\b', r'\1-\2-\3', text)
        
        # 处理 MM/DD/YYYY 格式
        text = re.sub(r'\b(\d{1,2})/(\d{1,2})/(\d{4})\b', r'\3-\1-\2', text)
        
        # 处理中文日期格式
        chinese_date_pattern = r'(\d{4})年(\d{1,2})月(\d{1,2})日'
        def convert_chinese_date(match):
            year, month, day = match.groups()
            return f"{year}-{int(month):02d}-{int(day):02d}"
        
        text = re.sub(chinese_date_pattern, convert_chinese_date, text)
        
        return text

# 使用示例
if __name__ == "__main__":
    # 数据提取示例
    print("=== 数据提取示例 ===")
    sample_text = """
    今天是2023-12-25,也是12/25/2023。
    现在时间是14:30:25。
    商品价格:¥1,234.56 和 $99.99。
    折扣率:15.5% 和 20%。
    姓名:张三,年龄:25岁。
    姓名:李四,年龄:30岁。
    联系电话:138-1234-5678,13912345679,+86 137 1234 5678
    """
    
    extractor = DataExtractor()
    formatter = DataFormatter()
    
    print("原文本:")
    print(sample_text)
    
    print("\n提取的日期:", extractor.extract_dates(sample_text))
    print("提取的时间:", extractor.extract_times(sample_text))
    print("提取的价格:", extractor.extract_prices(sample_text))
    print("提取的百分比:", extractor.extract_percentages(sample_text))
    print("提取的人员信息:", extractor.extract_structured_data(sample_text))
    
    print("\n=== 数据格式化示例 ===")
    print("标准化电话号码:")
    formatted_text = formatter.normalize_phone_numbers(sample_text)
    print(formatted_text)
    
    print("\n标准化日期:")
    standardized_dates = formatter.standardize_dates(sample_text)
    print(standardized_dates)
    
    print("\n格式化货币:")
    prices = extractor.extract_prices(sample_text)
    for price in prices:
        formatted_price = formatter.format_currency(price)
        print(f"  {price} -> {formatted_price}")

小结与回顾

本章我们深入学习了正则表达式的基础知识和在Python中的应用:

  1. 正则表达式概念:理解了正则表达式作为一种描述文本模式的形式化语言,具有简洁、灵活、高效的特点。

  2. 基本元字符:掌握了常用的元字符如.^$*+?等的含义和用法。

  3. 字符类和预定义字符集:学会了使用字符类[]和预定义字符集\d\w\s等来匹配特定类型的字符。

  4. 量词和贪婪匹配:理解了贪婪匹配和非贪婪匹配的区别,以及如何使用量词控制匹配次数。

  5. 分组和捕获:掌握了分组的使用方法,包括捕获分组、非捕获分组和命名分组。

  6. Python re模块:熟悉了re模块的主要函数如match()search()findall()sub()等的使用。

  7. 匹配对象和标志:了解了匹配对象的属性和常用标志的作用。

通过本章的学习和实战练习,你应该已经掌握了正则表达式的基础知识,并能够在实际项目中运用正则表达式解决文本处理问题。正则表达式是一项非常实用的技能,在数据清洗、日志分析、文本验证等场景中都有广泛应用。

练习与挑战

基础练习

  1. 编写正则表达式匹配以下模式:

    • IPv6地址
    • MAC地址(如 00:1A:2B:3C:4D:5E)
    • 邮政编码(中国6位数字)
    • 身份证号码校验
  2. 使用re模块实现以下功能:

    • 提取HTML文档中的所有链接
    • 验证强密码(包含大小写字母、数字、特殊字符,长度至少8位)
    • 解析CSV格式的文本数据
  3. 创建一个文本处理工具,支持:

    • 批量替换文本中的敏感信息
    • 统计文档中各单词的出现频率
    • 提取文档中的所有电子邮件地址和电话号码

进阶挑战

  1. 开发一个完整的日志分析系统,支持多种日志格式的解析和统计分析
  2. 实现一个配置文件解析器,支持自定义语法和变量替换
  3. 创建一个代码注释提取工具,支持多种编程语言的注释格式
  4. 设计一个文本搜索引擎,支持正则表达式查询和高亮显示

项目实战

开发一个"智能文本处理平台",集成以下功能:

  • 多种文本格式的导入和导出(TXT、CSV、JSON、XML等)
  • 正则表达式模式库,支持常用模式的快速应用
  • 可视化正则表达式构建器,降低使用门槛
  • 批量文本处理功能,支持自定义处理流程
  • 实时预览和调试功能,方便验证正则表达式效果
  • 插件系统,支持扩展自定义处理模块

扩展阅读

  1. Python官方文档 - re模块: docs.python.org/zh-cn/3/lib…

    • 官方re模块的详细文档,包含所有函数和方法的说明
  2. 《精通正则表达式》 by Jeffrey E.F. Friedl:

    • 正则表达式的权威著作,深入讲解正则表达式的原理和应用
  3. Regex101: regex101.com/

    • 在线正则表达式测试工具,支持多种语言和实时调试
  4. RegExr: regexr.com/

    • 另一个优秀的在线正则表达式学习和测试平台
  5. Real Python - Regular Expressions: realpython.com/regex-pytho…

    • 提供高质量的Python正则表达式教程和实际应用案例
  6. MDN Web Docs - 正则表达式: developer.mozilla.org/zh-CN/docs/…

    • JavaScript正则表达式的详细文档,概念相通
  7. 《Python数据科学手册》by Jake VanderPlas:

    • 包含使用正则表达式进行数据清洗和处理的实际案例
  8. 正则表达式可视化工具: regexper.com/

    • 将正则表达式可视化为图形,帮助理解复杂表达式的结构

通过深入学习这些扩展资源,你将进一步巩固对正则表达式的理解,并掌握更多高级用法和最佳实践。正则表达式虽然看起来复杂,但通过不断练习和应用,你会逐渐掌握这项强大的文本处理工具。