🧙♂️ 第11天:正则表达式:字符串匹配的魔法咒语
🔍 欢迎来到正则表达式的奇妙魔法世界!在这里,你将学习如何使用神秘的咒语(正则表达式)在复杂的文本森林中精准地找到你想要的宝藏(特定模式的字符串)。
🎯 魔法师的学习目标
- 掌握正则表达式的基本概念和魔法用途
- 学习正则表达式的基础咒语(元字符)和组合方式
- 能够使用Python的
re魔法卷轴来释放这些咒语 - 运用正则表达式解决实际的文本处理难题
- 了解正则表达式的高级魔法技巧和最佳实践
🧠 魔法知识小调查
在开始今天的魔法学习之前,让我们来做个小调查:
你是否听说过正则表达式?
- A. 完全没听说过
- B. 听说过但没使用过
- C. 简单使用过
- D. 已经熟练掌握
你觉得正则表达式最吸引你的地方是?
- A. 强大的文本匹配能力
- B. 简洁的语法
- C. 广泛的应用场景
- D. 挑战性(嘿嘿,魔法师就喜欢挑战!)
📝 请记住你的答案,课程结束后看看自己有多大进步!
✨ 正则表达式的魔法起源
正则表达式(Regular Expression,简称regex或regexp)是一种强大的字符串匹配魔法,它能帮助魔法师们(程序员)在浩瀚的文本海洋中快速找到、验证或替换特定模式的字符串。
正则表达式魔法的主要应用场景:
- 🔍 文本搜索和过滤(就像魔法追踪术)
- ✅ 数据验证(如邮箱、电话号码、URL等,就像身份识别魔法)
- 🔄 文本替换和格式化(就像变形术)
- 📊 数据提取和解析(就像寻宝术)
- 📝 日志分析(就像解读古代卷轴)
🎭 魔法师小剧场:正则表达式的日常应用
场景: 魔法学院的收发室里,小精灵们正在处理来自世界各地的魔法邮件...
🧝♂️ 收发室主管:今天的邮件太多了!我们需要快速筛选出有效的学生邮件、提取电话号码并清理地址中的多余空格!
🧚♀️ 年轻精灵:这听起来很复杂,我们怎么才能快速完成?
🧙♂️ 正则魔法师:不用担心!我有正则表达式的魔法!看我的:
# 验证邮箱地址的魔法咒语
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
# 提取电话号码的魔法咒语
phone_pattern = r"1[3-9]\d{9}"
# 清理空格的魔法咒语
clean_pattern = r"\s+"🧚♀️ 年轻精灵:太神奇了!这些咒语真的能工作吗?
🧙♂️ 正则魔法师:当然!让我演示给你们看...
📜 魔法工具准备:Python中的re模块
在Python的魔法世界中,我们使用内置的re魔法卷轴来释放正则表达式的魔力。
# 导入re魔法卷轴
import re🧪 魔法小实验:你好,正则表达式!
让我们来尝试一个简单的魔法实验,感受正则表达式的力量:
import re
# 定义一个简单的魔法咒语:匹配以"magic"开头的字符串
spell = "^magic"
# 测试一些字符串
texts = ["magic is amazing", "the magic spell", "Magic Wand"]
for text in texts:
if re.match(spell, text):
print(f"✨ 咒语生效!'{text}' 匹配成功!")
else:
print(f"❌ 咒语失效!'{text}' 匹配失败!")
# 试试忽略大小写的魔法
print("\n🌟 使用忽略大小写的魔法:")
for text in texts:
if re.match(spell, text, re.IGNORECASE):
print(f"✨ 咒语生效!'{text}' 匹配成功!")
else:
print(f"❌ 咒语失效!'{text}' 匹配失败!")🪄 基础魔法咒语:正则表达式语法
1. 普通魔法符文
大多数魔法符文在正则表达式中都表示它们本身,例如字母、数字、空格等。
a, b, c, ..., z, A, B, C, ..., Z, 0, 1, 2, ..., 9, 空格, !, @, #, 等2. 特殊魔法符号(元字符)
这些是具有特殊魔力的符号,它们可以表示一类字符或字符组合。
常用魔法符号:
| 魔法符号 | 魔法效果 | 魔法示例 |
|---|---|---|
. | 匹配任意单个字符(除换行符外) | a.c 可以匹配 "abc", "a1c", "a@c" 等 |
^ | 匹配字符串的开始 | ^hello 只匹配以 "hello" 开头的字符串 |
$ | 匹配字符串的结束 | world$ 只匹配以 "world" 结尾的字符串 |
* | 匹配前面的字符0次或多次 | ab* 可以匹配 "a", "ab", "abb", "abbb" 等 |
+ | 匹配前面的字符1次或多次 | ab+ 可以匹配 "ab", "abb", "abbb" 等,但不匹配 "a" |
? | 匹配前面的字符0次或1次 | ab? 可以匹配 "a" 或 "ab" |
{n} | 匹配前面的字符恰好n次 | a{3} 只能匹配 "aaa" |
{n,} | 匹配前面的字符至少n次 | a{2,} 可以匹配 "aa", "aaa", "aaaa" 等 |
{n,m} | 匹配前面的字符n到m次 | a{2,4} 可以匹配 "aa", "aaa", "aaaa" |
[] | 字符集,匹配方括号内的任意一个字符 | [abc] 可以匹配 "a", "b" 或 "c" |
[^] | 否定字符集,匹配不在方括号内的任意一个字符 | [^abc] 匹配除 "a", "b", "c" 外的任意字符 |
| | 逻辑或,匹配左边或右边的表达式 | cat|dog 可以匹配 "cat" 或 "dog" |
() | 分组,将多个字符作为一个整体 | (ab)+ 可以匹配 "ab", "abab", "ababab" 等 |
3. 快捷魔法咒语(字符类简写)
为了让魔法师们更便捷地施法,正则表达式提供了一些常用字符类的简写形式。
常用快捷魔法咒语:
| 快捷咒语 | 魔法效果 | 等价咒语 |
|---|---|---|
\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] |
\b | 匹配单词边界 | - |
\B | 匹配非单词边界 | - |
\n | 匹配换行符 | - |
\t | 匹配制表符 | - |
\r | 匹配回车符 | - |
4. 破除魔法的咒语(转义字符)
如果要匹配魔法符号本身,需要使用反斜杠(\)来破除其魔法。
\. 匹配点号本身
\* 匹配星号本身
\+ 匹配加号本身
\? 匹配问号本身
\[ 匹配左方括号本身
\] 匹配右方括号本身
\\ 匹配反斜杠本身🚀 释放魔法:Python中使用正则表达式
Python的re魔法卷轴提供了多种魔法咒语来处理正则表达式,下面我们来学习其中最常用的几个。
1. re.match()魔法:从头开始的匹配
re.match()魔法尝试从字符串的开头匹配一个模式。如果匹配成功,返回一个魔法匹配对象;如果匹配失败,返回None。
魔法咒语: re.match(pattern, string, flags=0)
import re
# 尝试匹配以"hello"开头的字符串
result = re.match("hello", "hello world")
if result:
print("✨ 匹配成功!")
print(f"🧩 匹配的内容: {result.group()}") # 输出匹配的内容
else:
print("❌ 匹配失败!")
# 尝试匹配不以"hello"开头的字符串
result = re.match("hello", "world hello")
if result:
print("✨ 匹配成功!")
else:
print("❌ 匹配失败!")2. re.search()魔法:全字符串搜索
re.search()魔法在整个字符串中搜索匹配模式。如果找到匹配,返回一个魔法匹配对象;如果没有找到匹配,返回None。
魔法咒语: re.search(pattern, string, flags=0)
import re
# 在整个字符串中搜索"world"
result = re.search("world", "hello world hello")
if result:
print("✨ 匹配成功!")
print(f"🧩 匹配的内容: {result.group()}")
print(f"📍 匹配的起始位置: {result.start()}")
print(f"📍 匹配的结束位置: {result.end()}")
else:
print("❌ 匹配失败!")3. re.findall()魔法:找出所有宝藏
re.findall()魔法在整个字符串中搜索匹配模式,返回所有匹配的结果列表。
魔法咒语: re.findall(pattern, string, flags=0)
import re
# 查找字符串中所有的数字
result = re.findall("\d+", "I have 2 apples and 5 oranges.")
print(f"💰 找到的数字: {result}") # 输出: ['2', '5']
# 查找字符串中所有的单词
result = re.findall("\w+", "Hello, world! How are you?")
print(f"📝 找到的单词: {result}") # 输出: ['Hello', 'world', 'How', 'are', 'you']4. re.sub()魔法:替换变形术
re.sub()魔法用于替换字符串中匹配的模式,就像变形术一样。
魔法咒语: re.sub(pattern, replacement, string, count=0, flags=0)
import re
# 将字符串中的数字替换为"NUM"
result = re.sub("\d+", "NUM", "I have 2 apples and 5 oranges.")
print(f"🔄 替换后的字符串: {result}") # 输出: "I have NUM apples and NUM oranges."
# 将字符串中的空格替换为逗号,最多替换2次
result = re.sub(" ", ",", "a b c d e", 2)
print(f"🔄 替换后的字符串: {result}") # 输出: "a,b,c d e"5. re.split()魔法:分割魔法
re.split()魔法用于根据匹配的模式分割字符串。
魔法咒语: re.split(pattern, string, maxsplit=0, flags=0)
import re
# 根据数字分割字符串
result = re.split("\d+", "a1b2c3d4e")
print(f"✂️ 分割后的结果: {result}") # 输出: ['a', 'b', 'c', 'd', 'e']
# 根据逗号或空格分割字符串,最多分割2次
result = re.split(",| ", "a,b c,d e", 2)
print(f"✂️ 分割后的结果: {result}") # 输出: ['a', 'b', 'c,d e']6. 魔法匹配对象的秘密
当re.match()或re.search()匹配成功时,会返回一个魔法匹配对象。这个对象隐藏着许多秘密!
魔法匹配对象的能力:
group(): 返回匹配的字符串(魔法的核心)start(): 返回匹配的起始位置(魔法开始的地方)end(): 返回匹配的结束位置(魔法结束的地方)span(): 返回匹配的起始和结束位置的元组(魔法的范围)
import re
# 匹配一个数字和后面的字母
result = re.search("(\d+)([a-zA-Z]+)", "abc123def456ghi")
if result:
print(f"🧩 完整匹配: {result.group()}") # 输出: "123def"
print(f"🧩 第一个分组: {result.group(1)}") # 输出: "123" (第一个捕获组)
print(f"🧩 第二个分组: {result.group(2)}") # 输出: "def" (第二个捕获组)
print(f"📍 匹配的起始位置: {result.start()}") # 输出: 3
print(f"📍 匹配的结束位置: {result.end()}") # 输出: 9
print(f"📏 匹配的位置范围: {result.span()}") # 输出: (3, 9)💡 魔法师的实用咒语:正则表达式的实际应用
1. 验证邮箱地址(身份验证魔法)
import re
def validate_email(email):
"""验证邮箱地址是否合法的魔法"""
# 邮箱地址的正则表达式魔法
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return bool(re.match(pattern, email))
# 测试邮箱验证魔法
emails = [
"user@example.com",
"user.name@example.co.uk",
"user+tag@example.org",
"user@",
"@example.com",
"user@.com"
]
for email in emails:
if validate_email(email):
print(f"✅ {email} 是有效的邮箱地址")
else:
print(f"❌ {email} 是无效的邮箱地址")2. 验证手机号码(通讯验证魔法)
import re
def validate_phone(phone):
"""验证手机号码是否合法的魔法(中国手机号为例)"""
# 中国手机号的正则表达式魔法
pattern = r"^1[3-9]\d{9}$"
return bool(re.match(pattern, phone))
# 测试手机号验证魔法
phones = [
"13812345678",
"18687654321",
"12345678901",
"1381234567",
"138123456789"
]
for phone in phones:
if validate_phone(phone):
print(f"✅ {phone} 是有效的手机号码")
else:
print(f"❌ {phone} 是无效的手机号码")3. 提取URL中的域名(定位魔法)
import re
def extract_domain(url):
"""从URL中提取域名的定位魔法"""
pattern = r"https?://([a-zA-Z0-9.-]+)"
result = re.search(pattern, url)
if result:
return result.group(1)
else:
return None
# 测试域名提取魔法
urls = [
"https://www.example.com",
"http://subdomain.example.org/path",
"https://example.co.uk:8080/page",
"invalid-url"
]
for url in urls:
domain = extract_domain(url)
if domain:
print(f"URL: {url} 中的域名是: {domain}")
else:
print(f"无法从 {url} 中提取域名")4. 清理文本中的多余空格(整理魔法)
import re
def clean_whitespace(text):
"""清理文本中的多余空格的整理魔法"""
# 将多个空格替换为单个空格
text = re.sub(r"\s+", " ", text)
# 去除首尾的空格
return text.strip()
# 测试文本清理魔法
text = " Hello world! \t How are you? \n I'm fine. "
cleaned_text = clean_whitespace(text)
print(f"原始文本: '{text}'")
print(f"清理后的文本: '{cleaned_text}'")🔮 高级魔法技巧
1. 贪婪匹配与非贪婪匹配(贪心魔法与克制魔法)
正则表达式默认是贪婪的(贪心魔法),即尽可能多地匹配字符。可以通过在量词后面添加?来启用非贪婪匹配(克制魔法)。
import re
# 贪婪匹配(贪心魔法)
text = "<div>content1</div><div>content2</div>"
pattern = r"<div>.*</div>" # 默认贪婪匹配
result = re.search(pattern, text)
if result:
print(f"贪婪匹配结果: {result.group()}") # 输出: "<div>content1</div><div>content2</div>"
# 非贪婪匹配(克制魔法)
pattern = r"<div>.*?</div>" # 添加?启用非贪婪匹配
result = re.search(pattern, text)
if result:
print(f"非贪婪匹配结果: {result.group()}") # 输出: "<div>content1</div>"2. 预查魔法(预见未来的魔法)
预查是一种特殊的匹配方式,它不消耗字符,只检查匹配是否可能,就像预见未来一样!
- 正向预查 (
(?=...)): 匹配后面跟着特定模式的位置(预见未来) - 负向预查 (
(?!...)): 匹配后面不跟着特定模式的位置(预见不好的未来) - 正向后查 (
(?<=...)): 匹配前面是特定模式的位置(回顾过去) - 负向后查 (
(?<!...)): 匹配前面不是特定模式的位置(回顾不好的过去)
import re
# 正向预查:匹配后面跟着"@example.com"的用户名
text = "user1@example.com, user2@test.com, user3@example.com"
pattern = r"\w+(?=@example\.com)" # 正向预查,匹配后面是@example.com的用户名
results = re.findall(pattern, text)
print(f"正向预查结果: {results}") # 输出: ['user1', 'user3']
# 负向预查:匹配后面不跟着"@example.com"的用户名
pattern = r"\w+(?!@example\.com)@" # 负向预查,匹配后面不是@example.com的用户名
results = re.findall(pattern, text)
print(f"负向预查结果: {[r[:-1] for r in results]}") # 输出: ['user2']3. 魔法旗标(增强魔法的道具)
re模块的函数支持多种魔法旗标,可以改变正则表达式的行为。
常用的魔法旗标:
re.IGNORECASE或re.I: 忽略大小写(让魔法对大小写视而不见)re.MULTILINE或re.M: 多行模式(让魔法在每行都生效)re.DOTALL或re.S: 点号匹配任意字符,包括换行符(增强点号的魔法)re.VERBOSE或re.X: 启用详细模式,可以在正则表达式中添加注释和空格(让魔法咒语更易读)
import re
# 忽略大小写(让魔法对大小写视而不见)
text = "Hello World, hello python"
pattern = r"hello"
results = re.findall(pattern, text, re.IGNORECASE)
print(f"忽略大小写匹配结果: {results}") # 输出: ['Hello', 'hello']
# 多行模式(让魔法在每行都生效)
text = "line 1\nline 2\nline 3"
pattern = r"^line"
results = re.findall(pattern, text, re.MULTILINE)
print(f"多行模式匹配结果: {results}") # 输出: ['line', 'line', 'line']
# 详细模式(让魔法咒语更易读)
pattern = r"""\d{3} # 区号
- # 连字符
\d{3} # 前三位
- # 连字符
\d{4} # 后四位
"""
text = "我的电话号码是:123-456-7890"
result = re.search(pattern, text, re.VERBOSE)
if result:
print(f"详细模式匹配结果: {result.group()}") # 输出: "123-456-7890"🎮 高级魔法实践任务:文本处理魔法工具
任务: 你是一名高级魔法师,需要为魔法学院创建一个文本处理魔法工具!使用正则表达式实现以下功能:
- 🔍 提取文本中的所有电子邮件地址
- 📱 提取文本中的所有手机号码(中国手机号为例)
- 🌐 提取文本中的所有URL
- 🔢 将文本中的所有数字替换为
[数字] - 🧹 将文本中的多余空格清理为单个空格
示例文本:
sample_text = """联系我们:
邮箱: info@example.com, support@test.org
电话: 13812345678, 18687654321
网站: https://www.example.com, http://test.org/page
价格信息: 产品A 99元, 产品B 199.99元, 产品C 299元
这段文本 有很多 多余的 空格 和\t制表符
"""
# 你的魔法工具实现
import re
class TextMagicTool:
def __init__(self):
# 定义各种魔法咒语
self.email_pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
self.phone_pattern = r"1[3-9]\d{9}"
self.url_pattern = r"https?://[a-zA-Z0-9.-]+(?:/\S*)?"
self.whitespace_pattern = r"\s+"
self.number_pattern = r"\d+(?:\.\d+)?"
def extract_emails(self, text):
"""提取文本中的所有电子邮件地址"""
return re.findall(self.email_pattern, text)
def extract_phones(self, text):
"""提取文本中的所有手机号码"""
return re.findall(self.phone_pattern, text)
def extract_urls(self, text):
"""提取文本中的所有URL"""
return re.findall(self.url_pattern, text)
def replace_numbers(self, text):
"""将文本中的所有数字替换为[数字]"""
return re.sub(self.number_pattern, "[数字]", text)
def clean_whitespace(self, text):
"""清理文本中的多余空格"""
text = re.sub(self.whitespace_pattern, " ", text)
return text.strip()
# 使用魔法工具
magic_tool = TextMagicTool()
# 1. 提取邮箱
emails = magic_tool.extract_emails(sample_text)
print(f"📧 提取的邮箱地址: {emails}")
# 2. 提取手机号
phones = magic_tool.extract_phones(sample_text)
print(f"📱 提取的手机号码: {phones}")
# 3. 提取URL
urls = magic_tool.extract_urls(sample_text)
print(f"🌐 提取的URL: {urls}")
# 4. 替换数字
text_with_replaced_numbers = magic_tool.replace_numbers(sample_text)
print(f"🔄 替换数字后的文本:\n{text_with_replaced_numbers}")
# 5. 清理空格
cleaned_text = magic_tool.clean_whitespace(sample_text)
print(f"🧹 清理空格后的文本:\n{cleaned_text}")🎯 挑战升级!
如果你已经完成了基础任务,可以尝试这些高级挑战:
- 为你的魔法工具添加一个新功能:提取文本中所有的中文姓名(假设姓名为2-4个汉字)
- 添加一个功能,将日期格式从"YYYY-MM-DD"转换为"MM/DD/YYYY"
- 实现一个简单的HTML标签清理功能,移除文本中的所有HTML标签
🧠 魔法师小测试
让我们来测试一下你对正则表达式魔法的掌握程度!
1. 以下哪个正则表达式可以匹配任意一个数字?
- A.
[0-9] - B.
\d - C. 以上都是
- D. 以上都不是
2. 正则表达式中的^和$分别表示什么?
- A. 任意字符,空字符
- B. 字符串开始,字符串结束
- C. 行开始,行结束
- D. 匹配0次或多次,匹配1次或多次
3. 要匹配前面的字符恰好3次,应该使用以下哪个量词?
- A.
* - B.
+ - C.
? - D.
{3}
4. re.match()和re.search()的主要区别是什么?
- A.
re.match()只从字符串开头匹配,re.search()在整个字符串中搜索 - B.
re.match()返回匹配对象,re.search()返回匹配字符串 - C.
re.match()可以设置标志,re.search()不能 - D. 没有区别
5. 如何启用正则表达式的非贪婪匹配?
- A. 在模式前添加
non-greedy: - B. 在量词后添加
? - C. 使用
re.NON_GREEDY标志 - D. 无法启用非贪婪匹配
📝 答案将在课程结束时揭晓!
📝 今日魔法总结
今天我们学习了Python中的正则表达式魔法,包括:
正则表达式的基本概念和用途:
- 正则表达式是一种用于匹配字符串中字符组合的魔法模式
- 广泛应用于文本搜索、替换、验证等场景
正则表达式的基本语法:
- 普通字符:表示它们本身
- 元字符:具有特殊含义的字符,如
.、*、+、?等 - 字符类简写:如
\d、\w、\s等 - 转义字符:使用反斜杠
\来匹配元字符本身
Python中使用正则表达式:
re.match(): 从字符串开头匹配模式re.search(): 在整个字符串中搜索匹配re.findall(): 找出所有匹配的结果re.sub(): 替换匹配的模式re.split(): 根据匹配的模式分割字符串
正则表达式的实际应用:
- 验证邮箱地址
- 验证手机号码
- 提取URL中的域名
- 清理文本中的多余空格
正则表达式的高级特性:
- 贪婪匹配与非贪婪匹配
- 正向预查和负向预查
- 标志参数
正则表达式是一种强大而复杂的魔法,掌握它需要时间和练习。通过不断地使用和实践,你将能够编写出更加精确和高效的正则表达式,解决各种复杂的文本处理问题。
🔮 魔法师小测试答案
- C - 以上都是
- B - 字符串开始,字符串结束
- D -
{3} - A -
re.match()只从字符串开头匹配,re.search()在整个字符串中搜索 - B - 在量词后添加
?
🚀 明日魔法预告
在下一节课中,我们将学习Python的迭代器和生成器,这是一种能够高效处理大量数据的魔法!你将学习如何创建自己的迭代器,如何使用生成器来优化内存使用,以及它们在实际项目中的应用。准备好迎接新的魔法挑战吧!




