正则表达式(Regular Expression)是编程中处理文本模式的利器,在Python中通过内置的re模块实现。无论是数据清洗、日志解析还是表单验证,掌握正则都能大幅提升效率。本文将系统梳理Python正则表达式的核心用法,从基础元字符到高级预查与命名分组,并给出性能优化建议和典型应用场景的代码示例。
基础元字符与匹配
元字符是正则表达式的基石。常用元字符包括:
. 匹配任意单个字符(除换行符)
^ 匹配字符串开头
$ 匹配字符串结尾
* 匹配前一个字符0次或多次
+ 匹配前一个字符1次或多次
? 匹配前一个字符0次或1次
{n} 匹配恰好n次
{n,} 匹配至少n次
{n,m} 匹配n到m次
[] 匹配括号内任意字符
| 表示或
示例:匹配a与b中间任意字符的模式- pattern = r"a.b"
- text = "aab abb acb"
- matches = re.findall(pattern, text)
- print(matches) # 输出: ['aab', 'abb', 'acb']
复制代码
字符类
预定义字符集能快速匹配数字、字母、空白等:
\d 数字 [0-9]
\D 非数字
\w 字母数字下划线 [a-zA-Z0-9_]
\W 非字母数字下划线
\s 空白字符(空格、制表符、换行)
\S 非空白字符
样例:匹配美国社会安全号码格式- pattern = r"\d{3}-\d{2}-\d{4}"
- text = "My SSN is 123-45-6789."
- match = re.search(pattern, text)
- if match:
- print("Found SSN:", match.group()) # 输出Found SSN: 123-45-6789
复制代码
分组与捕获
圆括号()用于分组并捕获匹配内容,通过group(n)获取子组。- pattern = r"(\d{3})-(\d{2})-(\d{4})"
- text = "My SSN is 123-45-6789."
- match = re.search(pattern, text)
- if match:
- print("Full match:", match.group(0)) # 123-45-6789
- print("Group 1:", match.group(1)) # 123
- print("Group 2:", match.group(2)) # 45
- print("Group 3:", match.group(3)) # 6789
复制代码
非捕获分组(?:...)用于分组但不捕获,减少内存占用。- pattern = r"(?:\d{3})-(\d{2})-(\d{4})"
- match = re.search(pattern, text)
- print(match.group(0)) # 123-45-6789
- print(match.group(1)) # 45 (原第二组)
- print(match.group(2)) # 6789
复制代码
贪婪与非贪婪匹配
默认量词是贪婪的,尽量多匹配;在量词后加?变为非贪婪。- pattern_greedy = r"<.*>"
- pattern_non_greedy = r"<.*?>"
- text = "<html><head><title>Page Title</title></head></html>"
- match_greedy = re.search(pattern_greedy, text)
- match_non_greedy = re.search(pattern_non_greedy, text)
- print("Greedy match:", match_greedy.group())
- # 输出整个字符串
- print("Non-greedy match:", match_non_greedy.group())
- # 输出第一个标签<html>
复制代码
查找与替换
re.sub()用于批量替换匹配文本。- pattern = r"\d+"
- text = "There are 3 apples and 5 oranges."
- result = re.sub(pattern, "X", text)
- print(result) # There are X apples and X oranges.
复制代码
编译正则表达式
对于多次使用的正则,提前编译成Pattern对象可提升性能。- pattern = re.compile(r"\d{3}-\d{2}-\d{4}")
- match = pattern.search(text)
复制代码
多行匹配与忽略大小写
使用re.MULTILINE让^和$匹配每行开头结尾。- pattern = r"^\d+"
- text = "1 apple\n2 oranges\n3 bananas"
- matches = re.findall(pattern, text, re.MULTILINE)
- print(matches) # ['1', '2', '3']
复制代码 re.IGNORECASE使匹配忽略大小写。- re.search(r"apple", "Apple", re.IGNORECASE).group() # 'Apple'
复制代码
高级技巧:预查与命名分组
正向预查(?=...)匹配后面紧跟指定模式的内容,但不消耗该模式。- pattern = r"\d+(?= dollars)"
- text = "I have 100 dollars."
- print(re.search(pattern, text).group()) # 100
复制代码 负向预查(?!...)匹配后而不紧跟指定模式的内容。- pattern = r"\d+(?! dollars)"
- text = "I have 100 euros."
- print(re.search(pattern, text).group()) # 100
复制代码 命名分组(?P<name>...)让分组有可读名称,便于维护。- pattern = r"(?P<area>\d{3})-(?P<group>\d{2})-(?P<serial>\d{4})"
- match = re.search(pattern, "SSN 123-45-6789")
- print(match.group("area")) # 123
- print(match.group("group")) # 45
- print(match.group("serial")) # 6789
复制代码
调试与性能优化
调试时加re.DEBUG标志查看解析树。- re.compile(r"\d{3}-\d{2}-\d{4}", re.DEBUG)
复制代码 优化建议:
- 优先使用非贪婪匹配,避免回溯爆炸
- 避免嵌套量词
- 多次使用同一正则时预编译
- 对于大型文本,用re.finditer()替代re.findall()以节省内存- pattern = re.compile(r"\d{3}-\d{2}-\d{4}")
- for match in pattern.finditer(text):
- print("Found:", match.group())
复制代码
常见应用场景代码
验证电子邮件地址:- pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
- email = "example@example.com"
- if re.match(pattern, email):
- print("Valid")
复制代码 提取URL:- pattern = r"https?://(?:www\.)?\S+"
- text = "Visit https://example.com for more info."
- print(re.findall(pattern, text)) # ['https://example.com']
复制代码 提取HTML标签(含标签名和内容):- pattern = r"<(\w+)[^>]*>(.*?)</\1>"
- html = "<h1>Title</h1><p>Paragraph</p>"
- print(re.findall(pattern, html))
- # [('h1', 'Title'), ('p', 'Paragraph')]
复制代码
正则表达式是文本处理的瑞士军刀,熟练运用元字符、分组、预查等特性,再配合编译与优化,可以高效解决多数匹配、提取与替换任务。上面的示例覆盖了日常开发中最常用的场景,推荐收藏以备查阅。 |