Python入门教程丨3.5 正则表达式

今天我们来学习 Python 里超实用的字符串匹配和正则表达式。这是处理文本数据的神器,无论是爬虫、数据清洗还是文本分析,都离不开它,我们从基础语法讲起,再到实战场景,深入体会正则的妙用。

1. re 库

正则表达式(Regular Expression,简称regex或regexp)是一种用来匹配字符串的强大工具。它由一串字符和特殊符号组成,用于描述或匹配一系列符合某种模式的字符串。正则表达式广泛应用于文本搜索、文本替换等操作中。Python中的re模块提供了对正则表达式的全面支持。

1.1 正则表达式基础语法

1.1.1 正则表达式基本元素
符号 描述 示例
. 匹配除换行符外的任意字符 a.c 可匹配 "abc", "a1c" 等
^ 匹配字符串的开始 ^hello 匹配以 "hello" 开始的字符串
$ 匹配字符串的结束 world$ 匹配以 "world" 结束的字符串
* 匹配前面子表达式 0 次或多次 ab*c 匹配 "ac", "abc", "abbc" 等
+ 匹配前面子表达式 1 次或多次 ab+c 匹配 "abc", "abbc" 等,但不匹配 "ac"
? 匹配前面子表达式 0 次或 1 次 colou?r 匹配 "color" 或 "colour"

.用来匹配除换行符外的任意字符

python 复制代码
import re

# 示例:匹配号码格式的字符串
text = "我的号码是 A123-456 和 B789+012"
pattern = r"A1..-4.."
match = re.search(pattern, text)
print(match.group())  # 输出:A123-456
# 这里的 "A1..-4.." 中的每 "." 都匹配一个字符,就像"万能胶" 

^匹配字符串的开始

python 复制代码
import re

# 示例:检查用户名是否以特定前缀开头
text = "user12345"
pattern = r"^user"  # 匹配以 "user" 开头的字符串
match = re.search(pattern, text)
print("用户名以 'user' 开头:", bool(match))

$匹配字符串的结束

python 复制代码
import re

# 示例:检查一段话是否以问句结束
text = "这是为什么呢?"
pattern = r"呢?$"  # 匹配以 "呢?" 结尾的字符串
match = re.search(pattern, text)
print("句子以 '呢?' 结尾:", bool(match))

*匹配前面子表达式 0 次或多次

python 复制代码
import re

# 示例:匹配某人吃了多少块披萨
text = "他吃了披萨pizza, pppppizza, 和 pizza!"
pattern = r"p*izza"  # 匹配 "pizza" 或 "pppppizza" 等
matches = re.findall(pattern, text)
print(matches)  # 输出:['pizza', 'pppppizza', 'pizza']

+匹配前面子表达式 1 次或多次

python 复制代码
import re

# 示例:匹配某人的电子邮件地址是否包含至少一个 @
text = "我的邮箱是 test@domain.com 和 another@test.com"
pattern = r"\w+@\w+\.\w+"  # 匹配包含至少一个 "@" 的邮箱地址
matches = re.findall(pattern, text)
print(matches)  # 输出:['test@domain.com', 'another@test.com']

?匹配前面子表达式 0 次或 1 次


1.1.2. 特殊字符集
符号 描述 示例
\d 匹配一个数字字符 \d\d\d-\d\d\d-\d\d\d\d 匹配电话号码格式
\D 匹配非数字字符 \D{3} 匹配三个非数字字符
\w 匹配字母、数字或下划线 \w+ 匹配单词
\W 匹配非字母、数字或下划线 \W+ 匹配非单词字符
\s 匹配任何空白字符 \s+ 匹配空格、制表符等

\d用来匹配一个数字字符

python 复制代码
import re
text = "我有6颗苹果!"
pattern = r"我有 \d+ 颗苹果"  # 匹配以数字表示的苹果数量
print(re.findall(pattern, text))  # 输出:['我有6颗苹果']

\D匹配非数字字符

python 复制代码
import re
text = "这是一段没有数字的文字!"
pattern = r"\D+"  # 匹配所有非数字字符
print(re.findall(pattern, text))  # 输出:['这是一段没有数字的文字!\n']
# 这里非数字字符的范围很广,包括了字母和标点

\w匹配字母、数字或下划线

python 复制代码
import re
text = "我的用户名是_user123"
pattern = r"\w+"  # 匹配用户名中的字母、数字和下划线
print(re.findall(pattern, text))  # 输出:['我的用户名是', '_user123']

\W匹配非字母、数字或下划线,即匹配特殊字符

python 复制代码
import re
text = "这是一段$带有特殊字符的文本!@#"
pattern = r"\W+"  # 匹配所有非字母、数字或下划线的字符
print(re.findall(pattern, text))  # 输出:['$', '!', '@#']

\s匹配任何空白字符

python 复制代码
import re
text = "这是 一段有 多余空格 的 文本。"
pattern = r"\s+"  # 匹配文本中的所有空白字符
print(re.findall(pattern, text))  # 输出:[' ', ' ', ' ', ' ']

1.2 re模块常用函数

函数 用法 示例
search() 扫描整个字符串并返回第一个成功的匹配 re.search(r'ain', 'The rain in Spain')
match() 尝试从字符串的起始位置匹配一个模式 re.match(r'The', 'The rain in Spain')
findall() 返回所有与模式匹配的列表 re.findall(r'ain', 'The rain in Spain')
sub() 替换所有匹配项为指定字符串 re.sub(r'Spain', 'France', 'The rain in Spain')
split() 根据模式分割字符串 re.split(r'\s+', 'The rain in Spain')

re.search() 用于在字符串中寻找第一个匹配的模式。如果找到匹配,返回一个匹配对象,否则返回 None

python 复制代码
import re
text = "Hello, World!"
pattern = r"World"
result = re.search(pattern, text)
if result:
    print("匹配成功!")
    print("匹配位置:", result.start(), result.end())
else:
    print("匹配失败!")
# 匹配成功!匹配位置: 7 12

re.findall() 用于找到字符串中所有匹配的模式,返回一个列表。

python 复制代码
import re
text = "I have 1 apple, 2 bananas, and 3 oranges"
pattern = r"\d+"
result = re.findall(pattern, text)
print(result)  # 输出:['1', '2', '3']

re.sub() 用于替换字符串中匹配的模式。它会将所有匹配的内容替换为指定的字符串。

python 复制代码
import re
text = "I have 1 apple, 2 bananas, and 3 oranges"
pattern = r"\d+"
replacement = "many"
result = re.sub(pattern, replacement, text)
print(result)  # 输出:I have many apple, many bananas, and many oranges

re.match() 专门用来匹配字符串开头 的部分,如果字符串的开头符合给定的模式,它会返回一个匹配对象;否则返回 None

python 复制代码
import re

# 示例:检查字符串是否以数字开头
text = "123 是一个数字"
pattern = r"\d+"  # 匹配一个或多个数字
# 使用 re.match()
result = re.match(pattern, text)
if result:
    print("匹配成功!匹配的内容是:", result.group())
else:
    print("匹配失败!")
#运行结果:匹配成功!匹配的内容是: 123

re.split() 可以根据指定的正则表达式模式将字符串分割成多个部分,并返回一个列表。

python 复制代码
import re

# 示例:按逗号或空格分割字符串
text = "apple, banana orange,grape"
pattern = r"[,\s]+"  # 匹配逗号或空格
# 使用 re.split()
result = re.split(pattern, text)
print("分割后的结果:", result)
# 分割后的结果: ['apple', 'banana', 'orange', 'grape']

1.3 复杂模式

正则表达式不仅能匹配单个字符,还能通过一些特殊符号和语法构建复杂的匹配模式。下面我们来介绍几个常用的复杂模式构建方法。

符号 用法 示例
{n} 匹配前一字符恰好 n 次 a{2} 匹配 "aa"
{n,} 至少匹配 n 次 a{2,} 匹配 "aa", "aaa" 等
{n,m} 最少匹配 n 次且最多 m 次 a{2,3} 匹配 "aa", "aaa"
[abc] 字符集合,匹配括号内任一字符 [abc] 匹配 "a", "b", 或 "c"

{n}:该模式表示前一个字符必须出现恰好 n 次。

python 复制代码
import re

# 示例:匹配恰好 4 个连续的 'a'
text = "aaaa"
pattern = r"a{4}"
match = re.match(pattern, text)
print(match.group() if match else "No match")  # 输出:aaaa

{n,}:至少匹配 n 次,该模式表示前一个字符至少出现 n 次,可以无限次。

python 复制代码
import re

# 示例:匹配至少 2 个连续的 'a'
text = "aaaaaaaaaaa"
pattern = r"a{2,}"
match = re.match(pattern, text)
print(match.group() if match else "No match")  # 输出:aaaaaaaaaaa

{n,m}:该模式表示前一个字符至少出现 n 次,但不超过 m 次。

python 复制代码
import re

# 示例:匹配 1 到 3 个连续的 'a'
text = "aaabb"
pattern = r"a{1,3}"
match = re.match(pattern, text)
print(match.group() if match else "No match")  # 输出:aaa

[abc]:该模式表示匹配括号内的任一字符。

python 复制代码
import re

# 示例:匹配 'a' 或 'b' 或 'c'
text = "apple"
pattern = r"[abc]"
match = re.search(pattern, text)
print(match.group() if match else "No match")  # 输出:a

🎯Tips:

对于非常复杂的模式,建议分步骤构建并测试每个部分,想一口吃撑胖子很容易出错,分而治之是很好的思想。


1.4 贪婪与非贪婪模式

正则表达式中的贪婪模式和非贪婪模式是两种不同的匹配方式。在贪婪模式下,匹配尽可能多的字符,而非贪婪模式则匹配尽可能少的字符。

例如,对于字符串 "aaaab",模式 "a+"(贪婪)将匹配整个 "aaaa",而 "a+?"(非贪婪)将每次匹配一个 "a"。

1.4.1 贪婪模式

默认情况下,正则表达式的量词(如 *, +, ?, {n,}, {n,m})都是贪婪的,会尽可能多地匹配字符。

python 复制代码
import re

text = "abbbc"
pattern = r"ab+"  # 贪婪模式,匹配尽可能多的 b
match = re.search(pattern, text)
print(match.group())  # 输出:abbb
# 这里的正则表达式会匹配尽可能多的 'b',因为它是贪婪的。
1.4.2 非贪婪模式

在量词后加上问号?,即可启用非贪婪模式,匹配尽可能少的字符。

python 复制代码
import re

text = "abbbc"
pattern = r"ab+?"  # 非贪婪模式,匹配尽可能少的 b
match = re.search(pattern, text)
print(match.group())  # 输出:ab
# 这里的正则表达式会匹配最少的 'b',即只匹配一个 'b'。

在实际使用中,我们通常:

  • 使用 * 等贪婪匹配来处理简单的字符串操作。
  • 当需要精确控制匹配范围时,使用非贪婪模式来避免过度匹配。

例如我们在匹配网页源码时,匹配标签中的内容:

python 复制代码
text = "I have <div>Hello, <span>World!</span></div>"
pattern_greedy = r"<.*>"
pattern_non_greedy = r"<.*?>"

result_greedy = re.findall(pattern_greedy, text)
result_non_greedy = re.findall(pattern_non_greedy, text)

print("贪婪匹配结果:", result_greedy)  # 输出:['<div>Hello, <span>World!</span></div>']
print("非贪婪匹配结果:", result_non_greedy)  # 输出:['<div>', '<span>']

2. 实际应用

2.1 邮箱/手机号格式验证

在用户注册或数据输入时,验证邮箱和手机号的格式是非常重要的。我们可以用正则表达式来实现。

(1)邮箱验证📧

python 复制代码
import re

def validate_email(email):
    pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
    if re.match(pattern, email):
        return True
    else:
        return False

email = "test@exa@mple.com"
if validate_email(email):
    print("邮箱格式正确!")
else:
    print("邮箱格式错误!")

(2)手机号验证📱

python 复制代码
import re
def validate_phone(phone):
    pattern = r"^1[3-9]\d{9}$"
    if re.match(pattern, phone):
        return True
    else:
        return False

phone = "13812345678"
if validate_phone(phone):
    print("手机号格式正确!")
else:
    print("手机号格式错误!")

(3)手机号提取📱

python 复制代码
import re
phone_pattern = r"(?<!\d)(1[3-9]\d{9})(?!\d)"
text = "紧急联系13812345678,备用19198765432"
phones = re.findall(phone_pattern, text)
print("提取到的手机号:", phones)  
#提取到的手机号: ['13812345678', '19198765432']

2.2 HTML 文件解析

在爬虫或数据提取中,经常需要从 HTML 中提取内容。正则表达式可以帮助我们快速提取标签中的内容。

python 复制代码
import re

html = """
<div class='news'>
    <h1>今日热榜</h1>
    <p>Python 3.12发布啦!</p>
</div>
"""

# 提取所有标签内容
contents = re.findall(r"<.*?>(.*?)</.*?>", html)
print("HTML内容提取:", contents)  # ['今日热榜', 'Python 3.12发布啦!']

# 提取指定标签内容
h1_content = re.search(r"<h1>(.*?)</h1>", html).group(1)
print("头条标题:", h1_content)  # 今日热榜

2.3 日志分析

在处理日志文件时,我们可能需要提取错误信息。正则表达式可以帮助我们快速过滤出错误信息。

python 复制代码
log = """
[ERROR] 2023-08-10 14:22:35 数据库连接失败
[INFO] 2023-08-10 14:23:10 用户登录成功
[WARNING] 2023-08-10 14:24:02 内存使用率80%
"""

# 提取错误信息
errors = re.findall(r"\[ERROR\].+", log)
print("系统错误记录:", errors)  # ['[ERROR] 2023-08-10 14:22:35 数据库连接失败']

2.4 综合案例:简历解析

我们有一个简历文本,现在要使用正则提取其中的主要信息,保存到 json 中。

完整代码:

python 复制代码
import re

resume = """
姓名:张三
电话:138-1234-5678
邮箱:zhangsan@example.com
技能:Python, 正则表达式, 数据分析

项目经验:
1. <项目名称>智能客服系统</项目名称>
   <描述>使用正则表达式实现对话指令解析</描述>
2. <项目名称>日志分析平台</项目名称>
   <描述>开发基于正则的日志过滤模块</描述>
"""

# 信息提取
def parse_resume(text):
    info = {
        "name": re.search(r"姓名:(.*)", text).group(1),
        "phone": re.search(r"电话:(.*)", text).group(1),
        "email": re.search(r"邮箱:(.*)", text).group(1),
        "skills": re.findall(r"技能:(.+)", text)[0].split(", "),
        "projects": re.findall(r"<项目名称>(.*?)</项目名称>\s+<描述>(.*?)</描述>", text)
    }
    return info

print(parse_resume(resume))

运行结果:

{'name': '张三', 'phone': '138-1234-5678', 'email': 'zhangsan@example.com', 'skills': ['Python', '正则表达式', '数据分析'], 'projects': [('智能客服系统', '使用正则表达式实现对话指令解析'), ('日志分析平台', '开发基于正则的日志过滤模块')]}

3. 辅助开发工具

在学习和使用正则表达式时,可以使用在线测试工具,如 regex101.com。它支持多种编程语言的正则表达式测试,还能提供详细的匹配结果和解释,非常方便。

🔧 RegEx 调试神器地址: regex101.com

  • 实时高亮匹配 → 高亮匹配结果
  • 解释模式 → 自动翻译正则语法
  • 测试用例库 → 自带常见场景用例

4. 小结

正则表达式是文本处理的强大工具,掌握了它,你就能轻松应对各种文本匹配和提取任务。在学习正则表达式的过程中,需要注意以下几点:

理解元字符的含义 :元字符是正则表达式的核心,需要熟练掌握各个元字符的含义和用法,如 .^$*+? 等。

注意特殊字符的转义 :在正则表达式中,一些特殊字符如 .*+ 等具有特殊含义,如果需要匹配这些字符本身,需要进行转义,即在字符前加上反斜杠 \

掌握常用函数的用法re 模块提供了许多常用的函数,如 search()match()findall()sub()split() 等,需要熟练掌握这些函数的用法和区别。

理解贪婪与非贪婪模式 :贪婪模式会尽可能多地匹配字符,而非贪婪模式则尽可能少地匹配字符。在实际应用中,需要根据具体需求选择合适的模式。

注意调试和优化 :在编写复杂的正则表达式时,要注意调试和优化,避免出现错误和性能问题。可以使用在线测试工具如 regex101.com 进行调试和测试。

多多练习,熟练掌握正则表达式,人人都能成为文本处理的高手!

相关推荐
m0_dawn6 分钟前
Python 3.11 69 个内置函数(完整版)
开发语言·python·数据分析
那雨倾城9 分钟前
使用OpenCV实现帧间变化检测:基于轮廓的动态区域标注
图像处理·python·opencv·计算机视觉·视觉检测
Colinnian15 分钟前
pytorch阶段性总结1
人工智能·pytorch·python
ADFVBM19 分钟前
MySQL自启动失败(MySQL不能开机自启)解决方案_MySQL开机自启疑难杂症解决,适用Win11Win10
数据库·mysql
m0_7482382720 分钟前
MySQL 实验1:Windows 环境下 MySQL5.5 安装与配置
windows·mysql·adb
黑色火種39 分钟前
Flask笔记
笔记·python·flask
weixin_307779131 小时前
Python Pandas带多组参数和标签的Oracle数据库批量数据导出程序
数据库·python·oracle·pandas
心 -1 小时前
MySQL事务
数据库·mysql
Jelena技术达人1 小时前
爬虫获取翻译文本接口:技术实现与应用实践
爬虫·python·php
binbinxyz1 小时前
【算法系列】快速排序详解
python·算法·排序算法