Python正则表达式使用指南

正则表达式的基础和应用

一、正则表达式核心语法(四大基石)

1. ​元字符(特殊符号)​
  • 定位符
    ^:匹配字符串开始位置
    $:匹配字符串结束位置
    \b:匹配单词边界​(如 \bword\b 匹配独立单词)
  • 字符类
    .:任意单个字符(默认不包括换行符)
    \d:数字(等价 [0-9]
    \w:字母、数字、下划线(等价 [a-zA-Z0-9_]
    \s:空白符(空格、Tab、换行等)
  • 转义符
    \:将特殊字符转为普通字符(如 \. 匹配真正的点号)
  • 2. ​量词(重复次数)​
  • *:0次或多次
  • +:1次或多次
  • ?:0次或1次
  • {n}:精确n次
  • {n,}:至少n次
  • {n,m}:n到m次
  • 3. ​字符集合与逻辑
  • [abc]:匹配a、b、c中的任意一个
  • [a-z]:匹配小写字母a到z
  • [^abc]:否定集合(匹配不在abc中的字符)
  • |:逻辑或(如 cat|dog 匹配"cat"或"dog")
  • 4. ​分组与引用
  • ( ):捕获分组(可通过 \1 或 $1 反向引用)
  • (?: ):非捕获分组(仅用于逻辑分组)
  • (?P<name>):命名分组(Python中可通过名称引用)

  • 二、正则引擎工作原理(NFA vs DFA)

    1. ​NFA引擎(主流实现)​
  • 特点:支持回溯、捕获组、零宽断言等高级功能,但存在性能风险
  • 匹配流程
    1. 从起始位置尝试匹配
    2. 记录所有可能的分支(回溯点)
    3. 失败时退回最近回溯点继续尝试
    4. 直到匹配成功或完全失败
  • 2. ​DFA引擎
  • 特点:无回溯,线性时间复杂度,但功能受限(不支持分组引用)
  • 流程:一次性扫描文本,无状态回退

  • 三、关键应用场景与解决方案

    1. ​数据验证(精准匹配)​
  • 邮箱验证

    ^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$  
  • ^ 和 $ 确保整行匹配
  • [\w\.-]+ 允许用户名包含字母、数字、点、减号
  • ([\w-]+\.)+ 匹配多级域名(如 "mail." 或 "google.com.")
  • [\w-]{2,4} 匹配顶级域名(如 com、org)
  • 强密码规则

    ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$  
  • (?=.*\d):正向预查确保包含数字
  • (?=.*[a-z]):必须有小写字母
  • (?=.*[A-Z]):必须有大写字母
  • .{8,}:总长度至少8位
  • 2. ​数据提取(捕获关键信息)​
  • 从URL提取域名和路径

    ^https?://([^/?#]+)([^?#]*)  
  • 分组1 ([^/?#]+) 捕获域名(如 www.example.com
  • 分组2 ([^?#]*) 捕获路径(如 /path/to/page
  • 日志时间戳提取

    \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}
  • 精确匹配 YYYY-MM-DD HH:MM:SS 格式
  • 3. ​文本清洗与替换
  • 删除HTML标签

    <[^>]+>  
  • 匹配所有以 < 开头、> 结尾的内容
  • 使用 re.sub(r'<[^>]+>', '', html) 替换为空
  • 格式化电话号码
    输入:1234567890 → 输出:(123) 456-7890

    re.sub(r'(\d{3})(\d{3})(\d{4})', r'(\1) \2-\3', phone)

  • 四、性能优化与避坑指南

    1. ​避免灾难性回溯
  • 危险模式(a+)+ 或 .*.*(嵌套量词导致指数级复杂度)
  • 优化方法
  • 用具体字符代替 .*(如 \d+ 代替 .*
  • 使用原子分组 (?>...)(部分引擎支持)
  • 添加锚点限制范围(如 ^...$
  • 2. ​贪婪与非贪婪选择
  • 贪婪模式​(默认):.* 匹配尽可能多内容
    <div>.*</div> → 可能跨多个标签错误匹配
  • 非贪婪模式.*? 匹配最短结果
    <div>.*?</div> → 精确匹配单个标签内容
  • 3. ​预编译与复用
    # 预编译提升性能(适用于频繁调用场景)
    pattern = re.compile(r'\d{3}-\d{4}')
    pattern.findall('Tel: 123-4567')

    五、进阶技巧

    1. ​零宽断言(Lookaround)​
  • (?=...):正向先行断言(右侧必须满足条件)
    \d+(?=px) → 匹配 "100px" 中的 "100"
  • (?<=...):正向后行断言(左侧必须满足条件)
    (?<=\$)\d+ → 匹配 "$200" 中的 "200"
  • 2. ​条件匹配
    (?(id)yes|no) → 根据分组是否存在选择分支
  • 示例:匹配带区号的电话号码
    ($)? \d{3} (?(1)$|) → 匹配 "(123)" 或 "123"

  • 六、好用的工具

    1. 调试工具
    2. RegExr:实时高亮匹配结果,显示分组与回溯
    3. Regex101:支持多语言引擎,提供错误解释
    4. 可视化工具
      Regexper:图形化展示正则逻辑

    七、总结

    正则表达式的核心在于 ​模式定义 与 ​引擎匹配机制 的结合。掌握以下要点即可应对90%的场景:

    1. 精准定位:用 ^$\b 约束边界
    2. 明确范围:用字符集合 [...] 和量词 {n,m} 减少模糊匹配
    3. 合理分组:通过 ( ) 提取关键数据
    4. 性能优先:避免嵌套量词,优先使用具体字符

    以下是Python中正则表达式的用法


    一、环境准备

    1. 导入re模块

    import re  # Python内置正则库

    2. 原始字符串(Raw String)

    正则表达式推荐使用原始字符串(前缀 r),避免Python字符串转义冲突:

    pattern = r'\d+'  # 正确:匹配数字
    pattern = '\\d+'   # 错误:需双重转义,可读性差

    二、四大核心方法

    1. re.match() – ​从开头匹配

  • 只匹配字符串开头,成功返回Match对象,否则返回None

  • text = "123abc"
    result = re.match(r'\d+', text)
    if result:
        print("匹配成功:", result.group())  # 输出: 123

    2. re.search() – ​全局搜索

  • 扫描整个字符串,找到第一个匹配项

  • text = "abc456def"
    result = re.search(r'\d+', text)
    print(result.group())  # 输出: 456

    3. re.findall() – ​查找所有匹配

  • 返回所有匹配结果的列表​(无分组时返回字符串列表)

  • text = "1a2b3c"
    numbers = re.findall(r'\d', text)
    print(numbers)  # 输出: ['1', '2', '3']

    4. re.sub() – ​替换匹配内容

  • 将匹配内容替换为指定字符串

  • text = "2023-01-01"
    new_text = re.sub(r'-', '/', text)
    print(new_text)  # 输出: 2023/01/01

    三、分组与捕获

    1. 基本分组 ( )

  • 用括号分组,通过group(index)提取

  • text = "John:30"
    pattern = r'(\w+):(\d+)'
    match = re.search(pattern, text)
    print(match.group(1))  # John
    print(match.group(2))  # 30

    2. 非捕获分组 (?: )

  • 分组但不捕获,节省内存

  • text = "apple orange"
    pattern = r'(?:a|an|the) (\w+)'  # 匹配冠词后的单词但不捕获冠词
    match = re.search(pattern, text)
    print(match.group(1))  # apple

    3. 命名分组 (?P<name>)

  • 为分组命名,提升可读性

  • text = "Date: 2023-08-15"
    pattern = r'Date: (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'
    match = re.search(pattern, text)
    print(match.group('year'))   # 2023
    print(match.groupdict())     # {'year': '2023', 'month': '08', 'day': '15'}

    四、高级功能

    1. 正则标志(Flags)

  • 控制匹配模式的全局行为

  • text = "Hello\nWorld"
    # 多行模式(^和$匹配每行开头结尾)
    re.findall(r'^\w+', text, flags=re.MULTILINE)  # 输出: ['Hello', 'World']
    # 忽略大小写
    re.findall(r'hello', text, flags=re.IGNORECASE)  # 输出: ['Hello']

    2. 预编译正则表达式

  • 提升重复使用时的性能

  • pattern = re.compile(r'\d{3}-\d{4}')  # 编译为RegexObject
    result = pattern.findall("Tel: 123-4567")  # ['123-4567']

    3. 替换时使用函数

  • 动态生成替换内容

  • def to_upper(match):
        return match.group().upper()
    
    text = "hello world"
    new_text = re.sub(r'\b\w', to_upper, text)
    print(new_text)  # 输出: Hello World

    五、经典实战案例

    1. 验证邮箱格式

    def validate_email(email):
        pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
        return re.match(pattern, email) is not None
    
    print(validate_email("user@example.com"))  # True
    print(validate_email("invalid.email@"))     # False

    2. 提取HTML链接

    html = '<a href="https://example.com">Link</a>'
    pattern = r'href="(https?://[^"]+)"'
    match = re.search(pattern, html)
    print(match.group(1))  # https://example.com

    3. 转换日期格式

    date = "2023-08-15"
    new_date = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\2/\3/\1', date)
    print(new_date)  # 08/15/2023

    六、避坑指南

    1. 贪婪匹配陷阱
      使用 .*? 非贪婪模式避免过度匹配:

      text = "<div>Hello</div><div>World</div>"
      re.findall(r'<div>(.*?)</div>', text)  # ['Hello', 'World']
    2. 特殊字符转义
      匹配 .* 等符号需转义:

      re.findall(r'3\.14', "pi=3.14")  # ['3.14']
    3. 性能优化
      避免在循环中重复编译正则,优先使用预编译。


    七、调试工具

    1. 在线测试
      RegExr:实时高亮匹配结果,显示分组信息

    2. 代码调试
      使用 re.DEBUG 标志查看正则解析过程:
      re.compile(r'\d+', re.DEBUG)

    作者:愚戏师

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python正则表达式使用指南

    发表回复