Skip to content

🧙‍♂️ 第11天:正则表达式:字符串匹配的魔法咒语

🔍 欢迎来到正则表达式的奇妙魔法世界!在这里,你将学习如何使用神秘的咒语(正则表达式)在复杂的文本森林中精准地找到你想要的宝藏(特定模式的字符串)。

🎯 魔法师的学习目标

  • 掌握正则表达式的基本概念和魔法用途
  • 学习正则表达式的基础咒语(元字符)和组合方式
  • 能够使用Python的re魔法卷轴来释放这些咒语
  • 运用正则表达式解决实际的文本处理难题
  • 了解正则表达式的高级魔法技巧和最佳实践

🧠 魔法知识小调查

在开始今天的魔法学习之前,让我们来做个小调查:

  1. 你是否听说过正则表达式?

    • A. 完全没听说过
    • B. 听说过但没使用过
    • C. 简单使用过
    • D. 已经熟练掌握
  2. 你觉得正则表达式最吸引你的地方是?

    • A. 强大的文本匹配能力
    • B. 简洁的语法
    • C. 广泛的应用场景
    • D. 挑战性(嘿嘿,魔法师就喜欢挑战!)

📝 请记住你的答案,课程结束后看看自己有多大进步!

✨ 正则表达式的魔法起源

正则表达式(Regular Expression,简称regex或regexp)是一种强大的字符串匹配魔法,它能帮助魔法师们(程序员)在浩瀚的文本海洋中快速找到、验证或替换特定模式的字符串。

正则表达式魔法的主要应用场景:

  • 🔍 文本搜索和过滤(就像魔法追踪术)
  • ✅ 数据验证(如邮箱、电话号码、URL等,就像身份识别魔法)
  • 🔄 文本替换和格式化(就像变形术)
  • 📊 数据提取和解析(就像寻宝术)
  • 📝 日志分析(就像解读古代卷轴)

🎭 魔法师小剧场:正则表达式的日常应用

场景: 魔法学院的收发室里,小精灵们正在处理来自世界各地的魔法邮件...

🧝‍♂️ 收发室主管:今天的邮件太多了!我们需要快速筛选出有效的学生邮件、提取电话号码并清理地址中的多余空格!

🧚‍♀️ 年轻精灵:这听起来很复杂,我们怎么才能快速完成?

🧙‍♂️ 正则魔法师:不用担心!我有正则表达式的魔法!看我的:

python
# 验证邮箱地址的魔法咒语
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魔法卷轴来释放正则表达式的魔力。

python
# 导入re魔法卷轴
import re

🧪 魔法小实验:你好,正则表达式!

让我们来尝试一个简单的魔法实验,感受正则表达式的力量:

python
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)

python
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)

python
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)

python
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)

python
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)

python
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(): 返回匹配的起始和结束位置的元组(魔法的范围)
python
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. 验证邮箱地址(身份验证魔法)

python
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. 验证手机号码(通讯验证魔法)

python
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中的域名(定位魔法)

python
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. 清理文本中的多余空格(整理魔法)

python
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. 贪婪匹配与非贪婪匹配(贪心魔法与克制魔法)

正则表达式默认是贪婪的(贪心魔法),即尽可能多地匹配字符。可以通过在量词后面添加?来启用非贪婪匹配(克制魔法)。

python
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. 预查魔法(预见未来的魔法)

预查是一种特殊的匹配方式,它不消耗字符,只检查匹配是否可能,就像预见未来一样!

  • 正向预查 ((?=...)): 匹配后面跟着特定模式的位置(预见未来)
  • 负向预查 ((?!...)): 匹配后面不跟着特定模式的位置(预见不好的未来)
  • 正向后查 ((?<=...)): 匹配前面是特定模式的位置(回顾过去)
  • 负向后查 ((?<!...)): 匹配前面不是特定模式的位置(回顾不好的过去)
python
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.IGNORECASEre.I: 忽略大小写(让魔法对大小写视而不见)
  • re.MULTILINEre.M: 多行模式(让魔法在每行都生效)
  • re.DOTALLre.S: 点号匹配任意字符,包括换行符(增强点号的魔法)
  • re.VERBOSEre.X: 启用详细模式,可以在正则表达式中添加注释和空格(让魔法咒语更易读)
python
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"

🎮 高级魔法实践任务:文本处理魔法工具

任务: 你是一名高级魔法师,需要为魔法学院创建一个文本处理魔法工具!使用正则表达式实现以下功能:

  1. 🔍 提取文本中的所有电子邮件地址
  2. 📱 提取文本中的所有手机号码(中国手机号为例)
  3. 🌐 提取文本中的所有URL
  4. 🔢 将文本中的所有数字替换为[数字]
  5. 🧹 将文本中的多余空格清理为单个空格

示例文本:

python
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}")

🎯 挑战升级!

如果你已经完成了基础任务,可以尝试这些高级挑战:

  1. 为你的魔法工具添加一个新功能:提取文本中所有的中文姓名(假设姓名为2-4个汉字)
  2. 添加一个功能,将日期格式从"YYYY-MM-DD"转换为"MM/DD/YYYY"
  3. 实现一个简单的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中的正则表达式魔法,包括:

  1. 正则表达式的基本概念和用途

    • 正则表达式是一种用于匹配字符串中字符组合的魔法模式
    • 广泛应用于文本搜索、替换、验证等场景
  2. 正则表达式的基本语法

    • 普通字符:表示它们本身
    • 元字符:具有特殊含义的字符,如.*+?
    • 字符类简写:如\d\w\s
    • 转义字符:使用反斜杠\来匹配元字符本身
  3. Python中使用正则表达式

    • re.match(): 从字符串开头匹配模式
    • re.search(): 在整个字符串中搜索匹配
    • re.findall(): 找出所有匹配的结果
    • re.sub(): 替换匹配的模式
    • re.split(): 根据匹配的模式分割字符串
  4. 正则表达式的实际应用

    • 验证邮箱地址
    • 验证手机号码
    • 提取URL中的域名
    • 清理文本中的多余空格
  5. 正则表达式的高级特性

    • 贪婪匹配与非贪婪匹配
    • 正向预查和负向预查
    • 标志参数

正则表达式是一种强大而复杂的魔法,掌握它需要时间和练习。通过不断地使用和实践,你将能够编写出更加精确和高效的正则表达式,解决各种复杂的文本处理问题。

🔮 魔法师小测试答案

  1. C - 以上都是
  2. B - 字符串开始,字符串结束
  3. D - {3}
  4. A - re.match()只从字符串开头匹配,re.search()在整个字符串中搜索
  5. B - 在量词后添加?

🚀 明日魔法预告

在下一节课中,我们将学习Python的迭代器和生成器,这是一种能够高效处理大量数据的魔法!你将学习如何创建自己的迭代器,如何使用生成器来优化内存使用,以及它们在实际项目中的应用。准备好迎接新的魔法挑战吧!

© 2025 技术博客. All rights reserved by 老周有AI