正则的本质:用模式描述文本
正则表达式(Regex)是一种描述文本模式的语言。与其说「找所有手机号」,不如用模式描述「1 开头,第二位是 3-9,后面再跟 9 个数字」——这就是正则干的事。
Python 用内置的 re 模块,零安装:
import re
text = "联系电话:13812345678,备用:19900001111"
phones = re.findall(r"1[3-9]\d{9}", text)
print(phones) # ['13812345678', '19900001111']
四个核心函数
import re
# 1. findall:找所有匹配,返回列表
re.findall(r"\d+", "我有3个苹果和25个橘子") # ['3', '25']
# 2. search:找第一个匹配,返回 Match 对象(没有则返回 None)
m = re.search(r"\d+", "我有3个苹果")
if m:
print(m.group()) # '3'
# 3. sub:替换
re.sub(r"\d+", "N", "我有3个苹果和25个橘子") # '我有N个苹果和N个橘子'
# 4. split:按模式切分
re.split(r"[,,]", "苹果,香蕉,橘子") # ['苹果', '香蕉', '橘子']
元字符速查(正则的字母表)
| 符号 | 含义 | 示例 |
|---|---|---|
. |
任意字符(除换行) | a.c 匹配 abc、a1c |
\d |
数字 | \d\d 匹配 42 |
\w |
字母/数字/下划线 | \w+ 匹配 hello_1 |
\s |
空白(空格/制表/换行) | |
^ $ |
行首 / 行尾 | ^hello 要求行首是 hello |
* |
前一个出现 0 次或多次 | ab* 匹配 a、ab、abbb |
+ |
前一个出现 1 次或多次 | ab+ 匹配 ab、abbb |
? |
前一个出现 0 次或 1 次 | colou?r 匹配 color 和 colour |
{n} {n,} {n,m} |
指定次数 | \d{4} 恰好 4 位数字 |
[...] |
字符集合 | [aeiou] 元音之一 |
[^...] |
排除集合 | [^0-9] 非数字 |
(...) |
分组(可提取) | 见下文 |
A|B |
或 | cat|dog |
大写是取反:\D 非数字,\W 非单词字符,\S 非空白。
写 Python 正则一定用原始字符串 r"...",否则 \d 会被 Python 先转义一层。
12 个实战模式(复制即用)
import re
# 1. 手机号
r"1[3-9]\d{9}"
# 2. 邮箱
r"[\w.+-]+@[\w-]+\.[\w.]+"
# 3. URL
r"https?://[\w.-]+(?:/[\w./?=&%-]*)?"
# 4. 身份证号(18位,简单校验)
r"\d{17}[\dXx]"
# 5. 日期 2026-09-29 或 2026/09/29
r"\d{4}[-/]\d{1,2}[-/]\d{1,2}"
# 6. 时间 14:30 或 14:30:05
r"\d{1,2}:\d{2}(?::\d{2})?"
# 7. IP 地址(简单版,不校验 0-255 范围)
r"(?:\d{1,3}\.){3}\d{1,3}"
# 8. 中文字符
r"[一-龥]+"
# 9. 纯数字(整数,可带负号)
r"-?\d+"
# 10. 小数(可带负号)
r"-?\d+\.?\d*"
# 11. HTML 标签
r"<[^>]+>"
# 12. 匹配汉字开头的用户名(2-6位中文或数字字母)
r"^[一-龥][\w一-龥]{1,5}$"
分组提取:不只匹配,还要拿出来
圆括号 () 分组后,可以分别提取每部分内容:
import re
log = "2026-09-29 14:30:05 [ERROR] 数据库连接失败"
# 三个分组:日期、时间、日志内容
pattern = r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)"
m = re.search(pattern, log)
if m:
date, time_, level, msg = m.groups()
print(date, time_, level, msg) # 2026-09-29 14:30:05 ERROR 数据库连接失败
命名分组更可读:
pattern = r"(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) \[(?P<level>\w+)\] (?P<msg>.+)"
m = re.search(pattern, log)
print(m.group("msg")) # 按名字取,不用数括号顺序
贪婪与非贪婪:最容易踩的坑
默认是贪婪匹配——尽可能多地吃字符:
import re
text = "<p>第一段</p><p>第二段</p>"
# 贪婪:从第一个 <p> 一直吃到最后一个 </p>
re.findall(r"<p>.*</p>", text)
# ['<p>第一段</p><p>第二段</p>'] ← 不是想要的!
# 非贪婪(加 ?):吃到最近的 </p> 就停
re.findall(r"<p>.*?</p>", text)
# ['<p>第一段</p>', '<p>第二段</p>'] ← 正确
记住:*、+、{n,m} 后面加 ? 就变非贪婪。解析 HTML/括号配对内容时,几乎总是用非贪婪。
编译复用与标志位
循环里反复用同一个正则,先编译一次提速:
PHONE_RE = re.compile(r"1[3-9]\d{9}") # 编译一次
for line in lines:
m = PHONE_RE.search(line) # 复用
常用标志位:
re.findall(r"hello", text, re.IGNORECASE) # 忽略大小写,HELLO 也能匹配
re.findall(r"^第.章", text, re.MULTILINE) # 多行模式:^ $ 匹配每行的首尾
re.findall(r"a.c", text, re.DOTALL) # 让 . 也能匹配换行符
什么时候不该用正则
正则很强,但有边界:
- 别用正则解析 HTML:HTML 不是规则语言,嵌套和异常写法会让正则崩溃。用 BeautifulSoup/lxml
- 别用正则校验复杂规则:密码强度「同时含大小写+数字+符号且不复读」,用代码逐项判断比一条超长正则清晰得多
- 简单字符串操作别上正则:
s.startswith()、s.split()、in能解决的,用它们更快更易读
练习与调试工具
- regex101.com:在线调试神器,粘贴文本和正则,实时高亮匹配、逐字符解释正则含义
- pythex.org:轻量替代
- 写复杂正则时,先写 3-5 个「应该匹配」和「不应该匹配」的测试用例,边写边验证
写在最后
正则不用「学会」再开始用——它是个查表工具。收藏本文的 12 个模式,遇到新需求时在 regex101 上边试边改,三次之后你就会发现自己已经不怎么看表了。







评论 (0)
还没有评论,快来抢沙发吧~