正则表达式的基础和应用
一、正则表达式核心语法(四大基石)
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引擎(主流实现)
- 特点:支持回溯、捕获组、零宽断言等高级功能,但存在性能风险
- 匹配流程:
- 从起始位置尝试匹配
- 记录所有可能的分支(回溯点)
- 失败时退回最近回溯点继续尝试
- 直到匹配成功或完全失败
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
)
- 分组1
日志时间戳提取
\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"
六、好用的工具
- 调试工具
- RegExr:实时高亮匹配结果,显示分组与回溯
- Regex101:支持多语言引擎,提供错误解释
- 可视化工具
Regexper:图形化展示正则逻辑
七、总结
正则表达式的核心在于 模式定义 与 引擎匹配机制 的结合。掌握以下要点即可应对90%的场景:
- 精准定位:用
^
、$
、\b
约束边界 - 明确范围:用字符集合
[...]
和量词{n,m}
减少模糊匹配 - 合理分组:通过
( )
提取关键数据 - 性能优先:避免嵌套量词,优先使用具体字符
以下是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
六、避坑指南
贪婪匹配陷阱
使用.*?
非贪婪模式避免过度匹配:text = "<div>Hello</div><div>World</div>" re.findall(r'<div>(.*?)</div>', text) # ['Hello', 'World']
特殊字符转义
匹配.
、*
等符号需转义:re.findall(r'3\.14', "pi=3.14") # ['3.14']
性能优化
避免在循环中重复编译正则,优先使用预编译。
七、调试工具
在线测试
RegExr:实时高亮匹配结果,显示分组信息- 代码调试
使用re.DEBUG
标志查看正则解析过程:re.compile(r'\d+', re.DEBUG)