正则表达式 – 使用总结
在日常开发中,我们经常需要处理字符串,比如验证邮箱格式、提取手机号、清洗日志数据、替换敏感词等。这些场景背后,往往离不开一个强大又“神秘”的工具——正则表达式。它就像一把瑞士军刀,虽然外形简单,却能应对各种复杂的字符串操作任务。
如果你刚开始接触正则表达式,可能会觉得它像天书,符号一堆,看得一头雾水。但只要掌握了基本逻辑和常用模式,你会发现它其实非常有规律,甚至能让你的代码变得简洁高效。今天,我们就来系统梳理一下正则表达式的核心用法,帮助你从“看不懂”到“用得上”。
字符匹配基础:从简单到复杂
正则表达式最基础的功能,就是匹配特定的字符。比如你想检查一个字符串是否包含字母 a,就可以写 a。这看起来很简单,但它的威力在于可以组合使用。
举个例子:你想匹配“cat”这个词,可以直接写 cat。但如果想匹配“cat”或“cut”,可以用 cat|cut,这里的 | 就是“或”的意思。
再比如,你想匹配任意一个数字,可以写 [0-9]。这个方括号表示“字符类”,即从里面选一个字符。你也可以写成 \d,这是更简洁的写法,代表任意一个数字字符。
import re
text = "今天是2024年5月1日"
pattern = r'\d' # \d 代表一个数字字符
matches = re.findall(pattern, text)
print(matches) # 输出: ['2', '0', '2', '4', '5', '1']
注释:这里的
r'\d'是原始字符串,避免反斜杠被转义。\d是正则中预定义的字符类,等价于[0-9]。
元字符与量词:控制匹配次数
正则表达式真正强大的地方,在于它能精确控制“匹配多少次”。这主要靠“量词”来实现。
常见的量词有:
*:匹配 0 次或多次+:匹配 1 次或多次?:匹配 0 次或 1 次{n}:精确匹配 n 次{n,}:至少匹配 n 次{n,m}:匹配 n 到 m 次
举个实际例子:验证一个手机号是否合法(中国手机号,11位,以1开头,第二位是3-9)。
import re
phone = "13812345678"
pattern = r'^1[3-9]\d{9}$'
if re.match(pattern, phone):
print("手机号格式正确")
else:
print("手机号格式错误")
注释:
^和$是锚点,确保整个字符串完全匹配。如果没有它们,1[3-9]\d{9}也可能匹配到“abc13812345678def”中的一部分,导致误判。
分组与捕获:提取关键信息
在实际项目中,我们不仅需要判断字符串是否符合格式,更常需要从中提取出有用的数据。这时,分组(())就派上用场了。
比如,从一段文本中提取日期(格式为 YYYY-MM-DD):
import re
text = "会议时间是2024-05-15,报名截止2024-06-30。"
pattern = r'(\d{4})-(\d{2})-(\d{2})'
matches = re.findall(pattern, text)
for year, month, day in matches:
print(f"年份: {year}, 月份: {month}, 日期: {day}")
注释:
(\d{4})表示匹配4位数字并作为一组,后面两个括号同理。re.findall返回的是元组列表,每个元组对应一组捕获内容。
你也可以用 re.search 找到第一个匹配项,并访问分组:
match = re.search(r'(\d{4})-(\d{2})-(\d{2})', text)
if match:
year = match.group(1) # 获取第一组
month = match.group(2) # 获取第二组
day = match.group(3) # 获取第三组
print(f"提取到日期:{year}年{month}月{day}日")
注释:
group(1)、group(2)等方法用于获取对应分组的内容,group(0)是完整匹配的字符串。
常用场景:正则表达式在项目中的实战
验证邮箱格式
邮箱的格式比较复杂,但用正则可以高效处理。一个常见的邮箱正则如下:
import re
email = "user@example.com"
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if re.match(pattern, email):
print("邮箱格式正确")
else:
print("邮箱格式错误")
注释:
^和$锚定整个字符串。[a-zA-Z0-9._%+-]+表示用户名部分,可以包含字母、数字、点、下划线、百分号、加号、减号。@后是域名,最后是顶级域名,至少两个字母。
提取HTML标签中的内容
在爬虫或日志分析中,经常需要从HTML中提取标签内容。比如提取所有 <p> 标签中的文字:
import re
html = "<p>这是第一个段落</p><p>这是第二个段落</p>"
pattern = r'<p>(.*?)</p>' # 非贪婪匹配
matches = re.findall(pattern, html)
for p in matches:
print(p)
注释:
.*?是非贪婪模式,表示尽可能少地匹配字符,避免把多个标签的内容合并在一起。如果用.*,可能会匹配到<p>第一个</p><p>第二个</p>,导致结果错误。
常见陷阱与最佳实践
正则表达式虽然强大,但也有“坑”需要注意。以下是几个常见问题:
1. 懒惰匹配 vs. 贪婪匹配
默认情况下,正则使用贪婪匹配,即尽可能多地匹配。但有时我们希望“最少匹配”。
比如,想提取 a...b...a...b 中第一个 a 到 b 之间的内容:
text = "a123b456b"
print(re.findall(r'a.*b', text)) # ['a123b456b']
print(re.findall(r'a.*?b', text)) # ['a123b']
注释:加
?后,量词变成懒惰模式,适合提取最短的匹配片段。
2. 转义特殊字符
正则中很多符号有特殊含义,比如 .、*、+、?、{、} 等。如果你要匹配这些字符本身,必须用反斜杠转义。
比如匹配一个实际的点号:
text = "文件名: config.json"
pattern = r'\.json' # 匹配 .json
print(re.findall(pattern, text)) # ['json']
注释:
\.表示匹配字面意义的点号,而不是“任意字符”。
总结与建议
正则表达式 – 使用总结,不是一朝一夕能掌握的,但它绝对是提升开发效率的重要技能。从最基础的字符匹配,到复杂的分组捕获、量词控制,再到实际项目中的验证与提取,每一步都有明确的应用场景。
建议初学者从简单开始,先掌握 ^、$、*、+、?、()、\d、\w 等常用符号,然后逐步尝试写一些小练习,比如:
- 验证身份证号(18位,最后一位可以是X)
- 提取URL中的域名
- 清洗日志中的错误码
随着实战经验积累,你会发现正则表达式不再神秘,反而成为你处理字符串问题的“利器”。
记住:正则表达式不是越复杂越好,而是越精确越高效。写完后,多测试几个边界 case,确保逻辑无误。
最后,别忘了:正则表达式 – 使用总结,不是终点,而是你编程能力进阶路上的一块重要基石。多练、多想、多查文档,你会越来越得心应手。