Python正则表达式6大进阶实战题
以下是 6道Python正则表达式进阶题,覆盖「分组捕获、零宽断言、贪婪/非贪婪、反向引用、Unicode匹配、替换回调」等核心进阶知识点,结合实际应用场景设计,附详细提示和解答:
题目1:复杂邮箱地址提取(分组+非捕获组+贪婪控制)
题目要求:
从混合文本中提取所有邮箱地址,需支持两种格式:
纯邮箱:user.name+tag@domain.co.uk(用户名含 .+_,域名支持多级子域)带显示名的邮箱:“张三” zhangsan-123@mail.qq.com 或 李四 lisi@company.io(显示名可能带引号/空格,邮箱用尖括号包裹)
提取目标:
每个邮箱需返回「显示名(无则为None)、用户名、完整域名」
输入示例:
text = '''
联系邮箱:"张三" <zhangsan-123@mail.qq.com>,备用邮箱 lisi+work@company.io,
还有 admin._root@sub.domain.com,以及错误格式 <invalid-email@.com>(需过滤)。
'''
输出示例:
[
("张三", "zhangsan-123", "mail.qq.com"),
(None, "lisi+work", "company.io"),
(None, "admin._root", "sub.domain.com")
]
涉及知识点:
非捕获组 (?😃、捕获组、贪婪/非贪婪匹配、字符类扩展、域名多级匹配
提示:
显示名部分:可选,可能带双引号,用 (?:[“']?(.?)[“']?s<)? 匹配用户名部分:允许 a-zA-Z0-9.±,用 [a-zA-Z0-9.±]+域名部分:支持多级子域(如 sub.domain.com),用 [a-zA-Z0-9-]+.[a-zA-Z0-9-.]+(注意避免匹配 .com. 这类非法结尾)
题目2:HTML标签内容提取与清理(零宽断言+多行模式)
题目要求:
从HTML片段中提取所有
…
标签内的文本(忽略标签内的子标签,如 )清理文本中的多余空格(连续空格合并为一个,首尾空格去除)
输入示例:
html = '''
<div class="header">标题</div>
<div class="content">
这是第一段内容,<span>包含子标签</span>,还有 多余 空格。
</div>
<div class="content">第二段内容,无子类<span>标签</span>,结尾有空格 </div>
<div class="footer">底部</div>
'''
输出示例:
[“这是第一段内容,包含子标签,还有 多余 空格。”, “第二段内容,无子类标签,结尾有空格”]
涉及知识点:
零宽断言(正向先行 (?<=)、反向先行 (?!))、多行模式 re.DOTALL、替换函数 re.sub
提示:
用正向先行断言匹配
开头,反向先行断言匹配
结尾,避免捕获标签本身用 re.DOTALL 让 . 匹配换行符(标签内可能换行)用 re.sub(r’s+', ’ ', text).strip() 清理空格
题目3:密码强度校验(正向先行断言+字符类)
题目要求:
设计正则表达式,校验密码是否满足以下所有条件:
长度 8-20 个字符必须包含至少 1 个大写字母(A-Z)必须包含至少 1 个小写字母(a-z)必须包含至少 1 个数字(0-9)必须包含至少 1 个特殊符号(!@#$%^&*()_±=[]{}|;:,.?~)不能包含空格、制表符等空白字符
输入示例:
passwords = ["Pass123!", "weakpassword", "Strong@2025", "Short1!", "NoSpecial123", "Has Space1!"]
输出示例:
[True, False, True, False, False, False]
涉及知识点:
正向先行断言 (?=)、字符类、长度限制 {m,n}、否定字符类 [^…]
提示:
用多个正向先行断言分别校验“必须包含的字符类型”用 [^s] 排除空白字符整体结构:^(?=.[A-Z])(?=.[a-z])(?=.d)(?=.[!@#
%^&*()_+-=[]{}|;:,.?~])[^\s]{8,20}
题目4:日志条目结构化解析(分组+多行匹配)
题目要求:
从服务器日志中提取每条日志的「时间戳、日志级别、模块名、错误信息」,日志格式如下:
时间戳:YYYY-MM-DD HH:MM:SS(如 2025-11-13 14:30:25)日志级别:INFO/WARN/ERROR/FATAL(大写,用方括号包裹)模块名:[模块名](如 [UserService]、[OrderDB])错误信息:冒号后到行尾的所有内容(可能包含空格、特殊符号)
输入示例:
log = '''
2025-11-13 14:30:25 [INFO] [UserService] 用户登录成功,ID:1001
2025-11-13 14:32:10 [WARN] [OrderDB] 数据库连接超时,重试第1次
2025-11-13 14:33:05 [ERROR] [PaymentAPI] 支付失败:金额格式错误(value=abc123)
2025-11-13 14:35:40 [FATAL] [SystemCore] 核心服务崩溃:内存溢出
'''
输出示例:
[
("2025-11-13 14:30:25", "INFO", "UserService", "用户登录成功,ID:1001"),
("2025-11-13 14:32:10", "WARN", "OrderDB", "数据库连接超时,重试第1次"),
("2025-11-13 14:33:05", "ERROR", "PaymentAPI", "支付失败:金额格式错误(value=abc123)"),
("2025-11-13 14:35:40", "FATAL", "SystemCore", "核心服务崩溃:内存溢出")
]
涉及知识点:
捕获组、字符类、多行模式 re.MULTILINE、非贪婪匹配
提示:
时间戳匹配:d{4}-d{2}-d{2} d{2}:d{2}:d{2}日志级别匹配:[(INFO|WARN|ERROR|FATAL)](用分组提取)模块名匹配:[(.*?)](非贪婪匹配方括号内内容)错误信息匹配::s*(.*)(冒号后所有内容,含空格和特殊符号)
题目5:重复内容去重与日期标准化(反向引用+替换回调)
题目要求:
去除文本中连续重复的单词(如 hello hello world → hello world,test test test → test)将文本中所有日期格式统一为 YYYY-MM-DD(支持原格式:YYYY/MM/DD、YYYY.MM.DD、YYYY-MM-DD)
输入示例:
text = '''
2025/11/13 今天天气很好,很好!我们我们讨论了 2025.10.01 的计划,
以及 2024-09-20 的会议记录,重复重复的单词需要需要去重。
'''
输出示例:
"2025-11-13 今天天气很好!我们讨论了 2025-10-01 的计划,以及 2024-09-20 的会议记录,重复的单词需要去重。"
涉及知识点:
反向引用 1、替换回调函数 re.sub(repl=func)、字符类 [./-]
提示:
连续重复单词匹配:(w+)s+1( 是单词边界,避免部分匹配,1 引用第一个分组的单词)日期标准化:用 re.sub(r’(d{4})./-./-‘, r’1-2-3’, text) 替换分隔符
题目6:Unicode多语言文本匹配(中文/Emoji识别)
题目要求:
从混合文本中提取所有「中文姓名」(2-4个中文字符,不含数字和特殊符号)提取所有Emoji表情(仅匹配常见表情,如 😊🎉🔥,排除中文符号)统计中文姓名和Emoji的数量
输入示例:
text = '''
参会人员:张三、李四华、王五123(非法)、赵六六六(4字)、陈五(2字)
会议情绪:😊 开心、🎉 顺利、🔥 紧急,还有中文符号:。,!(不算Emoji)
'''
输出示例:
{
"中文姓名": ["张三", "李四华", "赵六六六", "陈五"],
"Emoji": ["😊", "🎉", "🔥"],
"姓名数量": 4,
"Emoji数量": 3
}
涉及知识点:
Unicode属性匹配(p{})、字符类范围、re.UNICODE 模式
提示:
中文姓名匹配:[u4e00-u9fa5]{2,4}(u4e00-u9fa5 是中文Unicode范围)Emoji匹配:利用Unicode属性 p{Emoji}(需Python 3.7+,且正则模式加 re.UNICODE 或 re.U)排除非法姓名:用 (?![u4e00-u9fa5]*d) 正向先行断言排除含数字的姓名
解答(逐题解析)
题目1:复杂邮箱地址提取
import re
text = '''
联系邮箱:"张三" <zhangsan-123@mail.qq.com>,备用邮箱 lisi+work@company.io,
还有 admin._root@sub.domain.com,以及错误格式 <invalid-email@.com>(需过滤)。
'''
正则表达式:非捕获组控制显示名可选,捕获组提取关键信息
pattern = r’(?:[“‘]?(.?)[“']?s<)?([a-zA-Z0-9._±]+)@([a-zA-Z0-9-]+.[a-zA-Z0-9-.]+)(?:>)?’
解析:
(?:[“']?(.?)[“']?s<)? → 可选显示名(带引号/空格,非捕获组)
([a-zA-Z0-9._±]+) → 捕获用户名(允许合法特殊符号)
@ → 邮箱分隔符
([a-zA-Z0-9-]+.[a-zA-Z0-9-.]+) → 捕获多级域名
(?:>) → 可选的尖括号结尾(非捕获组)
matches = re.findall(pattern, text, re.DOTALL)
过滤非法域名(如 @.com)
result = []
for name, user, domain in matches:
if not domain.startswith(‘.’) and not domain.endswith(‘.’):
result.append( (name.strip() if name else None, user, domain) )
print(result)
输出:[(“张三”, “zhangsan-123”, “mail.qq.com”), (None, “lisi+work”, “company.io”), (None, “admin._root”, “sub.domain.com”)]
题目2:HTML标签内容提取与清理
import re
html = '''
<div class="header">标题</div>
<div class="content">
这是第一段内容,<span>包含子标签</span>,还有 多余 空格。
</div>
<div class="content">第二段内容,无子类<span>标签</span>,结尾有空格 </div>
<div class="footer">底部</div>
'''
正则:正向先行匹配开头标签,反向先行匹配结尾标签,DOTALL允许.匹配换行
pattern = r’(?<=
).*?(?=
)’
contents = re.findall(pattern, html, re.DOTALL)
清理空格和子标签
cleaned = []
for content in contents:
# 去除子标签(<.*?> 匹配任意标签)
no_tags = re.sub(r'<.*?>', '', content)
# 合并连续空格,去除首尾空格
cleaned_text = re.sub(r's+', ' ', no_tags).strip()
cleaned.append(cleaned_text)
print(cleaned)
输出:[“这是第一段内容,包含子标签,还有 多余 空格。”, “第二段内容,无子类标签,结尾有空格”]
题目3:密码强度校验
import re
def is_strong_password(password):
# 正则:多个正向先行断言校验条件,[^\s]排除空白字符
pattern = r'^(?=.*[A-Z])(?=.*[a-z])(?=.*d)(?=.*[!@#$%^&*()_+-=[]{}|;:,.?~])[^\s]{8,20}$'
return bool(re.match(pattern, password))
passwords = ["Pass123!", "weakpassword", "Strong@2025", "Short1!", "NoSpecial123", "Has Space1!"]
results = [is_strong_password(pwd) for pwd in passwords]
print(results)
输出:[True, False, True, False, False, False]
题目4:日志条目结构化解析
import re
log = '''
2025-11-13 14:30:25 [INFO] [UserService] 用户登录成功,ID:1001
2025-11-13 14:32:10 [WARN] [OrderDB] 数据库连接超时,重试第1次
2025-11-13 14:33:05 [ERROR] [PaymentAPI] 支付失败:金额格式错误(value=abc123)
2025-11-13 14:35:40 [FATAL] [SystemCore] 核心服务崩溃:内存溢出
'''
正则:捕获时间戳、级别、模块名、错误信息
pattern = r’(d{4}-d{2}-d{2} d{2}:d{2}:d{2}) [(INFO|WARN|ERROR|FATAL)] [(.?)] (.)’
matches = re.findall(pattern, log, re.MULTILINE)
print(matches)
输出与题目要求一致
题目5:重复内容去重与日期标准化
import re
text = '''
2025/11/13 今天天气很好,很好!我们我们讨论了 2025.10.01 的计划,
以及 2024-09-20 的会议记录,重复重复的单词需要需要去重。
'''
1. 日期标准化:替换 / . 为 –
text = re.sub(r'(d{4})[./-](d{2})[./-](d{2})', r'1-2-3', text)
2. 重复单词去重:用回调函数替换(处理多次重复,如 AAA → A)
def remove_duplicates(match):
return match.group(1)
单词边界,避免部分匹配(如 “aaaa” 不匹配 “aa aa”)
text = re.sub(r'(w+)s+1', remove_duplicates, text)
处理多次重复(如 “aaa aaa aaa” → “aaa”)
while re.search(r'(w+)s+1', text):
text = re.sub(r'(w+)s+1', remove_duplicates, text)
print(text.strip())
输出:“2025-11-13 今天天气很好!我们讨论了 2025-10-01 的计划,以及 2024-09-20 的会议记录,重复的单词需要去重。”
题目6:Unicode多语言文本匹配
import re
text = '''
参会人员:张三、李四华、王五123(非法)、赵六六六(4字)、陈五(2字)
会议情绪:😊 开心、🎉 顺利、🔥 紧急,还有中文符号:。,!(不算Emoji)
'''
1. 提取中文姓名(2-4字中文,不含数字)
name_pattern = r'(?![u4e00-u9fa5]*d)[u4e00-u9fa5]{2,4}'
names = re.findall(name_pattern, text)
2. 提取Emoji(Python 3.7+ 支持 p{Emoji})
emoji_pattern = r'p{Emoji}'
emojis = re.findall(emoji_pattern, text, re.UNICODE)
3. 统计结果
result = {
"中文姓名": names,
"Emoji": emojis,
"姓名数量": len(names),
"Emoji数量": len(emojis)
}
print(result)
输出:{“中文姓名”: [“张三”, “李四华”, “赵六六六”, “陈五”], “Emoji”: [“😊”, “🎉”, “🔥”], “姓名数量”: 4, “Emoji数量”: 3}
学习建议
每道题先自己尝试写正则,再对照解答,重点关注「为什么这么写」(如非捕获组的作用、断言的逻辑)用 re.findall() 验证匹配结果,用 re.match()/re.search() 调试单个匹配遇到复杂正则时,拆分片段逐步测试(如先匹配时间戳,再匹配日志级别,最后合并)结合实际场景记忆知识点(如密码校验用先行断言,日志解析用分组捕获)
通过这些题目,能熟练掌握Python正则的进阶用法,应对工作中的复杂文本处理场景!

