🌟 导读欢迎来到 Python 学习的第二十六站。
想象一个场景:老板甩给你一个几十万字的 Word 文档,里面杂乱无章地夹杂着客户的手机号、邮箱和身份证号。老板要求你:“10分钟内,把所有的手机号都提取出来!” 如果你用传统的 for 循环和 if 去挨个检查字符,不仅代码能写出上百行,而且还会漏掉很多。
这时候,就该请出编程界最著名的文本处理“核武器”——正则表达式(Regular Expression,简称 Regex)。 它虽然看起来像一堆外星乱码,但只要学会了它的规则,提取任何复杂文本都只需一行代码!今天,主编带你零基础、无痛拿下这座大山!🚀
🛡️ 1. 战前准备:引入 re 模块与魔法字母 r
在 Python 中使用正则表达式极其简单,只需要导入内置的 re 模块。
⚠️ 新手必看第一坑:为什么正则前面都要加个 r?在写正则表达式时,你会发现所有大牛的代码,在引号前面都会带一个字母 r(代表 raw,也就是“原汁原味”的意思),比如 r"\d+"。
为什么要这样写呢?因为在 Python 的普通字符串里,反斜杠 \ 具有“魔法(转义)”,比如 \n 代表换行。而正则表达式里也大量使用了 \ 作为符号。为了防止 Python 自作主张把你的正则符号翻译错,加上 r 就等于告诉电脑:“这段文字你不要瞎翻译,它是什么样就是什么样!原封不动地交给正则去处理!”
💡 通俗例子秒懂:
import re# 不加 r 的普通字符串,Python 看到 \n 会自作聪明地换行print("Hello\nWorld") # 输出结果直接断成了两截:# Hello# World# 加上 r 的原生字符串,\n 就被打回原形,只是 \ 和 n 两个普通字符!print(r"Hello\nWorld") # 输出结果:Hello\nWorld
(所以,以后写正则,永远养成加 r 的好习惯,能帮你避开 99% 的灵异报错!)
🧩 2. 拼图游戏:最核心的匹配规则(符号字典)
正则表达式其实就是一个“拼图模板”。它是由不同的符号拼凑起来,去长篇大论里“套”出你需要的内容。我们先来认识最常用的几块核心拼图,并结合具体的例子来理解:
🧑🤝🧑 第一类:代表“某种字符”的代号(单个字符替换)
. (英文句号):万能替身(除了换行符)。 它可以代表任何一个字、字母、符号。
\d (digit):代表 1 个数字(0到9)。
\w (word):代表 1 个“字母、数字或下划线”。 (常用来匹配账号名)
\s (space):代表 1 个空白。 (空格、Tab键等)
📦 第二类:范围选择 [ ]
方括号的意思是:“一个萝卜一个坑,只能从盒子里挑 1 个”。
🔢 第三类:控制数量的“修饰符” (跟在字符后面)
前面讲的 \d 每次只能匹配一个。如果有一串手机号,难道我们要写 11 次 \d 吗?当然不用,我们有数量修饰符!
* (星号):出现 0 次或无数次。 (可有可无,多多益善)
+ (加号):出现 1 次或无数次。 (至少得有 1 个,多多益善)
? (问号):出现 0 次或 1 次。 (仅仅代表“可选的”)
{n}:精确控制次数。
{n,m}:控制次数范围。
⚓ 第四类:定位钉子 ^ 和 $
它们不匹配任何文字,只代表位置边界。
^ (脱字符):代表整个文本的最开头。
$ (美元符):代表整个文本的最末尾。
🌟 极佳实战例子: 如果你做网页开发,要求用户输入 6 位数密码。
如果你只写 r"\d{6}",用户输入 "abc123456def" 也能通过(因为它只要包含了连续 6 个数字就行)。
必须写成 r"^\d{6}$"!这相当于加了两颗钉子钉死:从头到尾,必须完完全全只有 6 个数字,哪怕多一个空格也不行!
⚔️ 3. 核心大招:re 模块的 5 大常用方法
规则背熟了,我们该怎么指挥 Python 干活呢?re 模块为你准备了 5 把神兵利器,根据不同的需求来选择:
① findall():全部搜刮(新手最爱!⭐⭐⭐⭐⭐)
作用: 扫描整个长文本,找出所有符合规则的片段,直接打包成一个列表(List)交给你!
import retext = "张三的电话是 13812345678,李四的电话是 13987654321,公司前台:010-12345"# 规则解释:找 11 个连在一起的数字result = re.findall(r"\d{11}", text)print(result) # 极其舒爽的输出:['13812345678', '13987654321']
② search():满世界找,找到第一个就收工
作用: 扫描整个文本,一旦找到第一个匹配的,就立刻停下返回。⚠️ 进阶必坑点: 它返回的不是字符串,而是一个“匹配对象”。你必须调用 .group() 方法,才能把里面的文字抠出来!
import retext = "我的工号是 9527,你的工号是 10086"# 规则解释:找 1个以上的数字match_obj = re.search(r"\d+", text)if match_obj: # 必须用 .group() 才能看到真面目!它只找了第一个就停了! print("找到了:", match_obj.group()) # 输出:找到了: 9527
③ match():死心眼的门卫
作用: 极度严格!它只从字符串的最最最开头开始检查。如果开头第一个字不符合规则,直接判定失败(返回 None),后面有再多符合的它也不管!(也需要用 .group() 提取内容)
import retext = "工号 9527"# 试图找数字result1 = re.match(r"\d+", text) print(result1) # 输出 None!因为开头是中文“工”,不是数字,直接失败!result2 = re.search(r"\d+", text)print(result2.group()) # 换成 search 就能找到 9527 啦!
(💡 总结:search 是全屋搜索,match 是只查门口。日常提取信息用 search,校验开头的格式用 match。)
④ sub():超级无敌替换大法
作用: 类似于 Word 里的“查找并替换”,但它支持复杂的正则规则!语法:re.sub(正则规则, 替换成什么, 原文本)
import retext = "我的手机是13812345678,备用机是13987654321"# 需求:把所有的 11 位手机号,都替换成 "保密"safe_text = re.sub(r"\d{11}", "***保密***", text)print(safe_text)# 输出:我的手机是***保密***,备用机是***保密***
⑤ split():万能切割机
之前我们学过字符串的 .split(" ") 只能按某一个固定的字符切割。如果一句话里既有逗号,又有空格,还有分号,怎么把它完美切开?用正则!
import retext = "苹果,香蕉; 西瓜 草莓"# 规则解释:遇到 逗号、分号、空格(无论几个空格),都给我切开!# [\s,;]+ 意思是:盒子里的空白、逗号、分号,只要连续出现 1 次以上,就作为切割线fruits = re.split(r"[\s,;]+", text)print(fruits)# 完美输出:['苹果', '香蕉', '西瓜', '草莓']
📝 总结时刻:
正则表达式很难背,建议你把这篇文章收藏,用到的时候随时翻看这张总结表:
符号分类 | 核心符号 | 含义翻译 |
|---|
代表谁 | .
| 任意字符(非换行) |
| \d, \w, \s
| 数字、字母/数字/下划线、空白 |
| [abc]
| 盒子里挑一个 |
有多少个 | *, +, ?
| 0或无数个、1或无数个、0或1个 |
| {n}, {n,m}
| 精确n个、n到m个 |
三大方法 | findall()
| 找所有,返回列表(最推荐) |
| search() / match()
| 找第一个 / 从头找(记得用 .group() 提取文字) |
| sub() / split()
| 正则替换 / 正则切割 |
👇 互动时间
假设你要写一个程序,检查用户输入的是不是一个合法的年份(要求:必须是纯数字,且精确地只有 4 位数字,前后不能有其他乱七八糟的字符)。 你会使用下面哪一个正则表达式配合 re.match() 来进行严谨的校验呢?
A. r"\d" B. r"\d+" C. r"^\d{4}$" D. r"\d{4}"