当前位置: 首页 > article >正文

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. 调试工具
    • RegExr:实时高亮匹配结果,显示分组与回溯
    • Regex101:支持多语言引擎,提供错误解释
  2. 可视化工具
    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)


http://www.kler.cn/a/581426.html

相关文章:

  • LLMs基础学习(一)概念、模型分类、主流开源框架介绍以及模型的预训练任务
  • Vue计算侦听属性
  • AWS Bedrock 正式接入 DeepSeek-R1 模型:安全托管的生成式 AI 解决方案
  • 设计模式之原型模式:原理、实现与应用
  • 【Java】Websocket
  • Linux上位机开发实战(x86和arm自由切换)
  • SQLiteStudio:一款免费开源跨平台的SQLite管理工具
  • 【Troubleshot】Qt 长按按键 keyPressEvent keyReleaseEvent 自动重复问题
  • QT通过DeepSeek API获取公式正常显示的方法
  • postgresql 15.2 用pgbackup搭建备库后,主从复制一直无法启动
  • 卷积神经网络与计算机视觉:从数学基础到实战应用
  • LeetCode 1447 最简分数
  • flink依赖版本选择
  • jmeter:登录接口的token用于下一个接口
  • ArcGIS Pro中字段的新建方法与应用
  • Leetcode2012:数组美丽值求和
  • 一文读懂IEC103 转 Modbus TCP
  • 光电感知赋能智能未来 灵途科技护航新质生产力发展
  • APlayer漂亮的音乐播放器
  • vscode 源代码管理