则表达式(Regular Expressions,简称 regex 或 regexp)是一种强大的文本处理工具,可以用于搜索、匹配、替换、分割等操作。Python 的 re
模块提供了丰富的正则表达式功能。
一、正则表达式的基础知识
正则表达式是一种模式匹配工具,用于在文本中查找符合特定规则的字符串。以下是一些基本语法:
符号 | 描述 | 示例 | 匹配 |
---|---|---|---|
. |
任意单个字符(除换行符外) | a.b |
"aab", "acb" |
^ |
匹配字符串的开始 | ^Hello |
"Hello World" |
$ |
匹配字符串的结尾 | end$ |
"The end" |
* |
前一个字符重复 0 次或多次 | ab* |
"a", "ab", "abbb" |
+ |
前一个字符重复 1 次或多次 | ab+ |
"ab", "abbb" |
? |
前一个字符重复 0 次或 1 次 | colou?r |
"color", "colour" |
{n} |
前一个字符重复 n 次 | a{3} |
"aaa" |
{n,m} |
前一个字符重复 n 至 m 次 | a{2,4} |
"aa", "aaa", "aaaa" |
[] |
字符集,匹配其中任意一个字符 | [aeiou] |
"a", "e", "i" |
` | ` | 或,匹配任意一个规则 | `cat |
\d |
数字,等价于 [0-9] |
\d{3} |
"123", "456" |
\w |
字母、数字或下划线,等价于 [a-zA-Z0-9_] |
\w+ |
"word123" |
\s |
空白字符(包括空格、制表符等) | \s+ |
" ", "\t" |
二、re
模块的常用函数
Python 的 re
模块封装了一组方法,以下是常用方法及其作用:
1. re.match()
从字符串开头尝试匹配一个模式。
import re text = "hello world"
result = re.match(r"hello", text)
print(result.group()) # 输出: hello
2. re.search()
搜索整个字符串,返回第一个匹配项。
result = re.search(r"world", text)
print(result.group()) # 输出: world
3. re.findall()
返回所有匹配项的列表。
result = re.findall(r"\d+", "Order 123, Item 456")
print(result) # 输出: ['123', '456']
4. re.finditer()
返回一个迭代器,包含所有匹配项。
for match in re.finditer(r"\d+", "Order 123, Item 456"): print(match.group()) # 输出: 123, 456
5. re.sub()
替换匹配的子字符串。
result = re.sub(r"\d+", "#", "Order 123, Item 456")
print(result) # 输出: Order #, Item #
6. re.split()
按照匹配的模式拆分字符串。
result = re.split(r"\s+", "Split this string by spaces") print(result) # 输出: ['Split', 'this', 'string', 'by', 'spaces']
三、常见用法场景
1. 验证邮箱地址
email = "user@example.com"
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
if re.match(pattern, email):
print("Valid email")
else:
print("Invalid email")
2. 提取电话号码
text = "Call me at 123-456-7890 or 987-654-3210."
pattern = r"\d{3}-\d{3}-\d{4}"
matches = re.findall(pattern, text)
print(matches) # 输出: ['123-456-7890', '987-654-3210']
3. 清除HTML标签
html = "<p>This is a <b>bold</b> paragraph.</p>"
cleaned = re.sub(r"<.*?>", "", html)
print(cleaned) # 输出: This is a bold paragraph.
4. 分割多种分隔符的文本
text = "apple;orange,banana|grape"
fruits = re.split(r"[;|,]", text)
print(fruits) # 输出: ['apple', 'orange', 'banana', 'grape']
5. 密码强度检查
password = "Passw0rd!"
pattern = r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"
if re.match(pattern, password):
print("Strong password")
else:
print("Weak password")
四、正则表达式的高级技巧
1. 非捕获组与捕获组
- 捕获组:
(pattern)
,用于提取特定内容。 - 非捕获组:
(?:pattern)
,匹配但不捕获。
text = "2024-12-09"
pattern = r"(\d{4})-(\d{2})-(\d{2})"
match = re.match(pattern, text)
print(match.groups()) # 输出: ('2024', '12', '09')
2. 懒惰匹配与贪婪匹配
- 贪婪匹配:
.*
尽可能多地匹配。 - 懒惰匹配:
.*?
尽可能少地匹配。
text = "<tag>content</tag>"
greedy = re.search(r"<.*>", text).group()
lazy = re.search(r"<.*?>", text).group()
print(greedy) # 输出: <tag>content</tag>
print(lazy) # 输出: <tag>
3. 回溯控制
避免复杂模式导致的回溯过多可以提高效率。例如:
pattern = r"(a|b)+c"
text = "a" * 100 + "c"
match = re.match(pattern, text)
print(bool(match)) # 输出: True
五、性能优化建议
- 预编译正则表达式 如果同一个模式需要多次使用,建议预编译:
pattern = re.compile(r"\d+")
matches = pattern.findall("123 456 789")
-
避免过度复杂的模式 使用简单且明确的模式可以减少回溯。
-
使用原始字符串 避免转义混乱,始终使用
r"..."
格式定义正则表达式。
六、工具推荐与调试技巧
-
在线正则表达式测试工具
-
调试技巧
- 使用分步测试:先验证小的子模式,再逐步组合。
- 使用
re.DEBUG
查看正则表达式编译过程:re.compile(r"\d+", re.DEBUG)
七、总结
正则表达式是处理文本的利器,但也需要小心使用以避免过度复杂的模式和性能问题。通过合理使用 Python 的 re
模块和调试工具,可以有效地解决各种实际问题。