【Python-Day 42】解锁文本处理神技:Python 正则表达式 (Regex) 从入门到实战
Langchain系列文章目录
01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来
Python系列文章目录
PyTorch系列文章目录
机器学习系列文章目录
深度学习系列文章目录
Java系列文章目录
JavaScript系列文章目录
Python系列文章目录
01-【Python-Day 1】告别编程恐惧:轻松掌握 Python 安装与第一个程序的 6 个步骤
02-【Python-Day 2】掌握Python基石:变量、内存、标识符及int/float/bool数据类型
03-【Python-Day 3】玩转文本:字符串(String)基础操作详解 (上)
04-【Python-Day 4】玩转文本:Python 字符串常用方法深度解析 (下篇)
05-【Python-Day 5】Python 格式化输出实战:%、format()、f-string 对比与最佳实践
06- 【Python-Day 6】从零精通 Python 运算符(上):算术、赋值与比较运算全解析
07-【Python-Day 7】从零精通 Python 运算符(下):逻辑、成员、身份运算与优先级规则全解析
08-【Python-Day 8】从入门到精通:Python 条件判断 if-elif-else 语句全解析
09-【Python-Day 9】掌握循环利器:for 循环遍历序列与可迭代对象详解
10-【Python-Day 10】Python 循环控制流:while 循环详解与 for 循环对比
11-【Python-Day 11】列表入门:Python 中最灵活的数据容器 (创建、索引、切片)
12-【Python-Day 12】Python列表进阶:玩转添加、删除、排序与列表推导式
13-【Python-Day 13】Python 元组 (Tuple) 详解:从创建、操作到高级应用场景一网打尽
14-【Python-Day 14】玩转Python字典(上篇):从零开始学习创建、访问与操作
15-【Python-Day 15】深入探索 Python 字典 (下):常用方法、遍历、推导式与嵌套实战
16-【Python-Day 16】代码复用基石:详解 Python 函数的定义与调用
17-【Python-Day 17】玩转函数参数(上):轻松掌握位置、关键字和默认值
18-【Python-Day 18】玩转函数参数(下):*args 与 **kwargs 终极指南
19-【Python-Day 19】函数的回响:深入理解 return
语句与返回值
20-【Python-Day 20】揭秘Python变量作用域:LEGB规则与global/nonlocal关键字详解
21-【Python-Day 21】一行搞定!Python lambda 匿名函数的妙用与实战
22-【Python-Day 22】代码的基石:模块(Module)的导入与使用详解
23-【Python-Day 23】Python 模块化编程实战:创建、导入及 sys.path 深度解析
24-【Python-Day 24】告别杂乱代码!一文掌握 Python 包(Package)的创建与使用
25-【Python-Day 25】玩转数字:精通 math 与 random 模块,从数学运算到随机抽样
26-【Python-Day 26】解锁时间魔法:深入解析 time 与 datetime 模块
27-【Python-Day 27】轻松驾驭操作系统:精通 os 与 sys 模块核心功能
28-【Python-Day 28】从指令到蓝图:Python面向对象编程(OOP)入门指南
29-【Python-Day 29】万物皆对象:详解 Python 类的定义、实例化与 __init__
方法
30-【Python-Day 30】从 self、cls 到 @staticmethod:Python 面向对象三大方法深度解析
31-【Python-Day 31】一文搞懂 Python 实例属性与类属性:从定义、区别到应用场景
32-【Python-Day 32】面向对象基石之封装:从 __private
到 @property
的深度解析
33-【Python-Day 33】OOP核心之继承(Inheritance):代码复用与扩展的艺术
34-【Python-Day 34】深入解析Python继承:super()函数、MRO与菱形继承问题
35-【Python-Day 35】深入理解多态:代码更灵活的“鸭子类型”魔法
36-【Python-Day 36】解密文件IO:一文搞懂 Python 读写模式、编码与指针操作
37-【Python-Day 37】程序的守护者:一文彻底搞懂 Python 异常处理 (try-except-else-finally)
38-【Python-Day 38】告别通用错误!一文学会创建和使用 Python 自定义异常
39-【Python-Day 39】精通Python推导式:告别冗长for循环,提升代码效率与格调
40-【Python-Day 40】告别内存溢出!Python 生成器 (Generator) 的原理与实战
41-【Python-Day 41】揭秘函数的“魔法外衣”:一文彻底搞懂 Python 装饰器 (Decorator)
42-【Python-Day 42】解锁文本处理神技:Python 正则表达式 (Regex) 从入门到实战
文章目录
- Langchain系列文章目录
- Python系列文章目录
- PyTorch系列文章目录
- 机器学习系列文章目录
- 深度学习系列文章目录
- Java系列文章目录
- JavaScript系列文章目录
- Python系列文章目录
- 摘要
- 一、初识正则表达式:文本世界的“精准制导”
- 1.1 什么是正则表达式?
- 1.2 为什么需要学习正则表达式?
- 二、正则表达式的核心:元字符的奥秘
- 2.1 基础元字符:定位与匹配
- 2.2 量词元字符:控制重复次数
- 2.2.1 贪婪模式 vs. 非贪婪模式
- 2.3 字符集与其他特殊字符
- 2.4 预定义字符集
- 三、Python 的 `re` 模块实战
- 3.0.1 原始字符串 (Raw String)
- 3.1 核心函数概览
- 3.2 `re.match()`:从头开始匹配
- 3.2.1 语法与示例
- 3.3 `re.search()`:全局搜索第一个匹配
- 3.3.1 语法与示例
- 3.4 `re.findall()`:查找所有匹配
- 3.4.1 语法与示例
- 3.5 `re.sub()`:搜索与替换
- 3.5.1 语法与示例
- 四、进阶技巧与最佳实践
- 4.1 编译正则表达式:`re.compile()`
- 4.1.1 使用方法
- 4.2 分组与捕获:强大的 `()`
- 4.2.1 提取邮件的用户名和域名
- 五、总结
摘要
在处理字符串时,我们经常会遇到比简单查找和替换更复杂的需求,例如,验证一个字符串是否是合法的电子邮件地址、从一段文本中提取所有电话号码,或者将文本中所有符合特定模式的子串替换掉。对于这些任务,Python 内置的字符串方法可能显得力不从心。这时,强大的**正则表达式(Regular Expression, Regex)**就派上了用场。本文将带你从零开始,系统学习 Python 中的 re
模块,掌握正则表达式这一文本处理的终极利器,让你能够高效、精准地驾驭任何复杂的字符串操作。
一、初识正则表达式:文本世界的“精准制导”
在学习具体语法之前,我们先来理解正则表达式到底是什么。
1.1 什么是正则表达式?
想象一下,你想在成千上万份文档中找到所有的联系人电话。这些电话号码格式各异,有的是 138-1234-5678
,有的是 (010)12345678
,还有的是 139 1111 2222
。如果用普通的字符串查找,你可能需要写很多个 find()
,非常繁琐且容易遗漏。
正则表达式,就是一种用于描述、匹配一系列字符串共同特征的“模式模板”。它使用一套特殊的字符(称为元字符)来定义一个搜索规则,然后用这个规则去文本中进行匹配。对于上面的电话号码问题,我们可以用一个简单的正则表达式 \d{3,4}-?\d{7,8}
来匹配大多数情况。
简单来说,正则表达式为你提供了一套极其强大的“文本检索语法”,让你能用一个简洁的模式描述出你想要查找的内容。
1.2 为什么需要学习正则表达式?
- 高效精准:一个复杂的文本匹配任务,用正则表达式可能一行代码就能解决,而用传统方法可能需要数十行。
- 应用广泛:无论是数据清洗、网络爬虫、日志分析、还是后端开发中的数据校验,正则表达式都是不可或缺的工具。
- 语言通用:正则表达式是一套独立的规范,在 Python、Java、JavaScript、Go 等几乎所有主流编程语言中都有实现,学会它将终身受益。
在 Python 中,我们主要通过内置的 re
模块来使用正则表达式。
二、正则表达式的核心:元字符的奥秘
元字符是构成正则表达式的基石,它们是一些具有特殊含义的字符,用于定义匹配规则。下面我们将它们分类进行学习。
2.1 基础元字符:定位与匹配
元字符 | 名称 | 功能描述 | 示例 | 匹配对象 |
---|---|---|---|---|
. | 点 | 匹配除换行符 \n 之外的任意单个字符。 | p.p | p 和 p 之间有任意一个字符的字符串,如 “pyp”, “p_p”, “p9p”。 |
^ | 脱字符 | 匹配字符串的开头。 | ^Hello | 以 “Hello” 开头的字符串,如 “Hello World”。 |
$ | 美元符 | 匹配字符串的结尾。 | world$ | 以 “world” 结尾的字符串,如 “hello world”。 |
2.2 量词元字符:控制重复次数
量词用来指定它前面的一个字符或一个分组可以出现的次数。
元字符 | 名称 | 功能描述 | 示例 | 匹配对象 |
---|---|---|---|---|
* | 星号 | 匹配前面的子表达式零次或多次。 | go*d | “gd”, “god”, “good”, “gooood” 等。 |
+ | 加号 | 匹配前面的子表达式一次或多次。 | go+d | “god”, “good”, “gooood” 等 (不包括 “gd”)。 |
? | 问号 | 匹配前面的子表达式零次或一次。 | colou?r | “color” 或 “colour”。 |
{n} | 大括号 | 精确匹配前面的子表达式 n 次。 | \d{3} | 恰好 3 个数字,如 “123”。 |
{n,} | 大括号 | 匹配前面的子表达式至少 n 次。 | \d{2,} | 至少 2 个数字,如 “12”, “12345”。 |
{n,m} | 大括号 | 匹配前面的子表达式 n 到 m 次。 | \d{2,4} | 2 到 4 个数字,如 “12”, “123”, “1234”。 |
2.2.1 贪婪模式 vs. 非贪婪模式
默认情况下,*
, +
, {n,}
等量词是贪婪的(Greedy),它们会尽可能多地匹配字符。例如,对于字符串 "<html><h1>Title</h1></html>"
,正则表达式 <.*>
会匹配整个字符串,而不是只匹配 <html>
。
要切换到非贪婪模式(Non-Greedy),只需在量词后面加上一个 ?
。
*?
: 非贪婪匹配,匹配零次或多次,但尽可能少地匹配。+?
: 非贪婪匹配,匹配一次或多次,但尽可能少地匹配。{n,m}?
: 非贪婪匹配,匹配 n 到 m 次,但尽可能少地匹配。
示例:
import retext = "<html><h1>Title</h1></html>"# 贪婪模式
greedy_match = re.search(r"<.*>", text)
print(f"贪婪匹配结果: {greedy_match.group(0)}")# 非贪婪模式
non_greedy_match = re.search(r"<.*?>", text)
print(f"非贪婪匹配结果: {non_greedy_match.group(0)}")
输出:
贪婪匹配结果: <html><h1>Title</h1></html>
非贪婪匹配结果: <html>
2.3 字符集与其他特殊字符
元字符 | 名称 | 功能描述 | 示例 | 匹配对象 |
---|---|---|---|---|
[] | 字符集 | 匹配方括号中的任意一个字符。 | [abc] | “a”, “b”, “c” 中的任意一个。 |
[^] | 否定字符集 | 匹配任何不在方括号中的字符。 | [^abc] | 除了 “a”, “b”, “c” 之外的任意一个字符。 |
[a-z] | 范围 | 匹配指定范围内的任意一个字符。 | [0-9] , [A-Z] | 任意一个数字,任意一个大写字母。 |
` | ` | 或 | 匹配 ` | ` 左边或右边的表达式。 |
() | 分组 | 将括号内的表达式作为一个整体,并可以捕获这部分匹配的内容。 | (ab)+ | 一个或多个 “ab”,如 “ab”, “abab”。 |
\ | 转义符 | 将下一个字符标记为特殊字符或字面值。 | \. | 匹配真实的 . 字符,而不是任意字符。 |
2.4 预定义字符集
为了方便,正则表达式提供了一些预定义的字符集。
字符集 | 等价于 | 描述 |
---|---|---|
\d | [0-9] | 匹配一个数字字符。 |
\D | [^0-9] | 匹配一个非数字字符。 |
\w | [a-zA-Z0-9_] | 匹配包括下划线的任何单词字符。 |
\W | [^a-zA-Z0-9_] | 匹配任何非单词字符。 |
\s | [ \t\n\r\f\v] | 匹配任何空白字符,包括空格、制表符、换行符等。 |
\S | [^ \t\n\r\f\v] | 匹配任何非空白字符。 |
三、Python 的 re
模块实战
掌握了元字符后,我们来看看如何在 Python 中使用 re
模块进行实际操作。
3.0.1 原始字符串 (Raw String)
在 Python 中定义正则表达式时,强烈建议在模式字符串前加上 r
前缀,这表示它是一个“原始字符串”。原始字符串会忽略反斜杠 \
的转义作用,避免与 Python 的字符串转义规则冲突。
例如,要匹配一个 \
字符,正则表达式是 \\
。如果不用原始字符串,你需要写成 '\\\\'
(前两个 \
转义成一个 \
,后两个也一样),而使用原始字符串,只需写成 r'\\'
,更加直观。
3.1 核心函数概览
re
模块提供了几个核心函数来执行匹配操作:
re.match()
: 从字符串的起始位置开始匹配。re.search()
: 扫描整个字符串,找到第一个匹配项。re.findall()
: 找到所有匹配项,并以列表形式返回。re.sub()
: 查找并替换。
3.2 re.match()
:从头开始匹配
此函数尝试从字符串的起始位置匹配一个模式。如果不是起始位置匹配,match()
就返回 None
。
3.2.1 语法与示例
re.match(pattern, string, flags=0)
pattern
: 正则表达式模式。string
: 要匹配的字符串。flags
: 标志位,用于控制匹配方式(如忽略大小写)。
import retext1 = "hello world"
text2 = "python hello"pattern = r"hello"# 在 text1 中,'hello' 在开头,匹配成功
match1 = re.match(pattern, text1)
if match1:print(f"在 '{text1}' 中找到匹配: {match1.group(0)}") # .group(0) 获取整个匹配的字符串# 在 text2 中,'hello' 不在开头,匹配失败
match2 = re.match(pattern, text2)
if match2:print(f"在 '{text2}' 中找到匹配: {match2.group(0)}")
else:print(f"在 '{text2}' 中,模式 '{pattern}' 不在开头,match() 找不到匹配。")
输出:
在 'hello world' 中找到匹配: hello
在 'python hello' 中,模式 'hello' 不在开头,match() 找不到匹配。
3.3 re.search()
:全局搜索第一个匹配
search()
会扫描整个字符串,找到并返回第一个成功的匹配。如果找不到匹配,则返回 None
。
3.3.1 语法与示例
re.search(pattern, string, flags=0)
import retext1 = "hello world"
text2 = "python hello"pattern = r"hello"# 在 text1 中,找到第一个 'hello'
search1 = re.search(pattern, text1)
if search1:print(f"在 '{text1}' 中找到匹配: {search1.group(0)}")# 在 text2 中,即使 'hello' 不在开头,也能找到
search2 = re.search(pattern, text2)
if search2:print(f"在 '{text2}' 中找到匹配: {search2.group(0)}")
输出:
在 'hello world' 中找到匹配: hello
在 'python hello' 中找到匹配: hello
小结:match()
像是做“是不是以…开头”的判断,而 search()
则是做“包不包含…”的判断。
3.4 re.findall()
:查找所有匹配
当你想获取所有满足模式的子串,而不是仅仅第一个时,findall()
是最佳选择。它会返回一个包含所有匹配项的列表。
3.4.1 语法与示例
re.findall(pattern, string, flags=0)
应用场景:从文本中提取所有电话号码
import retext = "我的电话是 13812345678, 备用号码是 18987654321。请勿联系 110。"
# \d{11} 表示匹配 11 个连续的数字
phone_numbers = re.findall(r"\d{11}", text)print(f"提取到的所有电话号码: {phone_numbers}")
输出:
提取到的所有电话号码: ['13812345678', '18987654321']
3.5 re.sub()
:搜索与替换
sub()
用于查找所有匹配的子串,并用一个指定的字符串替换它们。
3.5.1 语法与示例
re.sub(pattern, repl, string, count=0, flags=0)
repl
: 替换成的字符串。count
: 最大替换次数,默认为0,表示全部替换。
应用场景:数据脱敏,隐藏手机号中间四位
import rephone = "13812345678"
# 模式解析:
# (\d{3}) - 第一个分组,匹配前3个数字
# \d{4} - 匹配中间4个数字 (不捕获)
# (\d{4}) - 第二个分组,匹配后4个数字
# 替换字符串 r'\1****\2' 中:
# \1 表示第一个分组捕获的内容 ('138')
# \2 表示第二个分组捕获的内容 ('5678')
safe_phone = re.sub(r"(\d{3})\d{4}(\d{4})", r"\1****\2", phone)print(f"原始号码: {phone}")
print(f"脱敏后号码: {safe_phone}")
输出:
原始号码: 13812345678
脱敏后号码: 138****5678
四、进阶技巧与最佳实践
4.1 编译正则表达式:re.compile()
如果一个正则表达式需要被重复使用多次,预先将其编译成一个正则表达式对象可以提高效率。因为 re.compile()
会将模式字符串转换成内部格式,后续的匹配操作就不再需要重新解析模式了。
4.1.1 使用方法
import re# 编译模式
email_pattern = re.compile(r'[\w.-]+@[\w.-]+\.\w+')emails = ["test.user@example.com","invalid-email","another_user@sub.domain.co.uk","user@localhost"
]valid_emails = []
for email in emails:# 使用编译好的对象进行匹配if email_pattern.match(email):valid_emails.append(email)print(f"有效的电子邮件地址: {valid_emails}")
输出:
有效的电子邮件地址: ['test.user@example.com', 'another_user@sub.domain.co.uk']
编译后的对象拥有与 re
模块相同的匹配函数(如 match()
, search()
, findall()
),只是不再需要传入 pattern
参数。
4.2 分组与捕获:强大的 ()
括号 ()
在正则表达式中有两个主要作用:
- 作为一个整体:如
(ab)+
匹配连续的ab
。 - 捕获内容:将括号内匹配到的内容保存起来,方便后续引用或提取。
match
和 search
返回的匹配对象(Match Object)提供了 group()
和 groups()
方法来获取捕获的内容。
group(0)
或group()
: 返回整个匹配的字符串。group(n)
: 返回第n
个分组捕获的内容(从1开始)。groups()
: 返回一个包含所有分组捕获内容的元组。
4.2.1 提取邮件的用户名和域名
import reemail = "contact-us@csdnblog.com"
# 模式解析:
# ([\w.-]+) - 第一个分组,捕获用户名部分
# @ - 匹配@符号
# ([\w.-]+) - 第二个分组,捕获域名部分
pattern = re.compile(r"([\w.-]+)@([\w.-]+)")match = pattern.search(email)
if match:print(f"完整匹配 (group 0): {match.group(0)}")print(f"用户名 (group 1): {match.group(1)}")print(f"域名 (group 2): {match.group(2)}")print(f"所有分组 (groups()): {match.groups()}")
输出:
完整匹配 (group 0): contact-us@csdnblog.com
用户名 (group 1): contact-us
域名 (group 2): csdnblog.com
所有分组 (groups()): ('contact-us', 'csdnblog.com')
这种能力在数据提取和解析任务中极为有用。
五、总结
正则表达式是 Python 乃至整个编程世界中处理文本的强大工具。今天,我们系统地学习了它的核心知识,让我们来回顾一下:
- 核心思想:正则表达式是一种定义“搜索模式”的语言,用于高效、灵活地匹配和处理字符串。在 Python 中,我们通过
re
模块来使用它。 - 元字符是基础:
.
,^
,$
,*
,+
,?
,[]
,|
,()
等元字符是构建正则表达式的积木,必须熟练掌握它们的含义,特别是量词的贪婪与非贪婪模式。 re
模块四大金刚:re.match()
:只从字符串开头匹配。re.search()
:在整个字符串中查找第一个匹配。re.findall()
:查找所有匹配,返回一个列表。re.sub()
:查找并替换。
- 最佳实践:
- 使用
r""
原始字符串来定义模式,避免转义混淆。 - 对于需要重复使用的模式,使用
re.compile()
进行预编译以提升性能。 - 善用
()
分组捕获功能,它可以精确提取你需要的特定部分数据。
- 使用
初学正则表达式可能会觉得其语法有些晦涩,但请不要畏惧。最好的学习方法就是不断练习。尝试为你日常遇到的文本处理问题构建正则表达式,你会很快发现它的强大之处,并将其变为你工具箱中不可或缺的一项神技。