1 概述
1.1 背景介绍
Python的正则表达式是一种强大的文本处理工具,它使用一种特殊的字符串模式来描述、匹配和查找一系列符合某个句法规则的字符串。在Python中,我们可以通过re模块来操作正则表达式。
通过本案例可以帮助我们快速掌握Python正则表达式相关运用。
1.2 适用对象
- 个人开发者
- 高校学生
1.3 案例时间
本案例总时长预计30分钟。
1.4 案例流程
{{{width="40%" height="auto"}}}
说明:
① 登录华为云,开通开发者空间,进入云主机,打开CodeArts IDE for Python创建工程; ② 编写代码运行。
1.5 资源总览
本案例预计花费总计0元。
| 资源名称 | 规格 | 单价(元) | 时长(分钟) |
|---|---|---|---|
| 开发者空间-云主机 | 2vCPUs | 4GB X86 CodeArts IDE for Python | 免费 | 30 |
2 准备开发环境
2.1 配置云主机并进入
参考“10分钟玩转云主机” 案例介绍中“2.2 申请云主机”章节内容完成华为开发者空间云主机申请与配置,配置云主机建议:“CPU架构”选择“X86”,“操作系统”选择“Ubuntu”。

然后点击进入桌面进入云主机。

2.2 使用CodeArts IDE创建工程
参考“初识云主机:CodeArts IDE入门”案例介绍“3.1 新建工程”章节新建工程。
新建的工程包含如下三个目录文件部分:
| 目录文件 | 说明 |
|---|---|
| .arts | CodeArts的配置文件 |
| venv | 虚拟环境 |
| main.py | 默认生成的入口文件 |

3 Python正则表达式
3.1 代码练习
下面我们主要围绕Python中re模块的常用方法及正则表达式常用规则进行说明与练习。
3.1.1 re模块
re模块(正则表达式模块)是用于字符串搜索、匹配、替换等操作的强大工具。它允许你使用正则表达式来定义文本的模式,然后对这些模式进行操作。部分常用方法如下: - re.match():该方法用于匹配字符串开头的模式,尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。 - re.search():该方法用于匹配出现在字符串中任意位置的模式,扫描整个字符串并返回第一个成功的匹配对象,否则返回None。 - re.findall():该方法返回字符串中制定正则表达式模式的所有非重叠匹配项,在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。 - re.sub():在字符串中找到匹配正则表达式模式的部分并替换为其他内容。 - re.split():按照能够匹配的子串将字符串分割后返回列表。
代码示例:
import re
# 1. re.match():学号严格校验(开头+结尾锚定)
while True:
stu_id = input("\n1. 输入学号(如PY2024001): ").strip().upper()
if re.match(r"^[A-Z]{2}\d{7}$", stu_id):
print(f"学号格式正确(专业:{stu_id[:2]})")
break
print("格式错误:2字母+7数字(如PY2024001)")
# 2. re.search():手机号非法字符定位
while True:
phone = input("2. 输入手机号(如13812345678): ").strip()
if re.search(r"\D", phone): # 搜索首个非数字字符
pos = re.search(r"\D", phone).start()
print(f"第{pos+1}位含非法字符:{phone[pos]}(请修正)")
else:
print("手机号无非法字符")
break
# 3. re.findall():批量提取选修课程
course_text = input("3. 输入选修课程(如'PY001,CS002;MATH003'): ").strip()
courses = re.findall(r"[A-Z]{2}\d{3}", course_text) # 提取2字母+3数字的课程码
print(f"提取课程:{courses}(共{len(courses)}门)")
# 4. re.sub():身份证脱敏处理(隐藏生日)
id_card = input("4. 输入身份证号(演示脱敏): ").strip()
masked_id = re.sub(r"(\d{6})\d{8}(\d{4})", r"\1********\2", id_card) # 分组替换
print(f"脱敏后:{masked_id}(隐藏生日信息)")
# 5. re.split():地址多分隔符解析
address = input("5. 输入地址(如'北京市;海淀区|学院路 37号'): ").strip()
parts = re.split(r"[;|,\t]+", address) # 支持;|,\t多种分隔符
print("解析结果:")
print(f" 省/市:{parts[0]}")
print(f" 区:{parts[1]}")
print(f" 详细:{parts[2]}")
3.1.2 语法规则--字符
以下是部分常用字符含义用法: - .:通配符,匹配任意单个字符(换行符除外)。 - \^:起始符,匹配字符串开始位置。 - \$:结束符,匹配字符串结束位置。 - |:逻辑或,匹配多个分支中的一个。 - \:转义符,转换字符含义。 - []:字符集,匹配集合中的任意单个字符。 - ():分组符,创建捕获组或改变优先级。 - \d:数字类,匹配数字字符,等同[0-9]。 - \D:非数字类,匹配非数字字符等同[\^0-9]。 - \w:单词类,匹配字母/数字/下划线,等同[a-zA-Z0-9_]。 - \W:非单词类,匹配非单词字符,等同[\^\w]。 - \s:空白类,匹配空白字符(空格、换行等),等同[ \t\n\r\f\v]。 - \S:非空白类,匹配非空白字符,等同[\^\s]。
代码示例:
import re
# 测试用多行文本
text = """
字符系统测试文本:
1. 基础匹配:a.c匹配abc、a+c
2. 边界测试:start行首与end行尾
3. 数字ID:user_01, user_02, user_03, user_1
4. 特殊符号:$100.5 | 50%折扣
5. 分组测试:2023-08-25 2024/12/31
6. 大小写单词:Hello world测试TEST
7. 空白检查:空格 制表符 换行
end
"""
# 1. . 匹配任意字符(换行符除外)
print("案例1(.):", re.findall(r'a.c', text))
# 输出:['a.c', 'abc', 'a+c']
# 2. ^ 匹配字符串开始
print("案例2(^):", re.search(r'^字符', text, re.M).group())
# 输出:字符(多行模式下匹配段落开头)
# 3. $ 匹配字符串结束
print("案例3($):", re.search(r'end$', text, re.M).group())
# 输出:end(匹配文本末尾的end)
# 4. | 逻辑或
print("案例4(|):", re.findall(r'\$100|50%', text))
# 输出:['$100', '50%']
# 5. \ 转义特殊字符
print("案例5(\):", re.findall(r'\$', text))
# 输出:['$']
# 6. [] 字符集合
print("案例6([]):", re.findall(r'user_0[1-3]', text))
# 输出:['user_01', 'user_02', 'user_03']
# 7. () 捕获分组
date_matches = re.findall(r'(\d{4})[-/](\d{2})[-/](\d{2})', text)
print("案例15(()):", date_matches)
# 输出:[('2023', '08', '25'), ('2024', '12', '31')]
# 8. \d 匹配数字
print("案例7(\d):", re.findall(r'user_\d', text))
# 输出:['user_0', 'user_0', 'user_0', 'user_1']
# 9. \D 匹配非数字
print("案例8(\D):", re.findall(r'\D', "123abc!@#")[:2])
# 输出: ['a', 'b']
# 10. \w 匹配单词字符
print("案例9(\w):", re.findall(r'\w', "Hello world_test"))
# 输出:['H', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '_', 't', 'e', 's', 't']
# 11. \W 匹配非单词字符
print("案例10(\W):", re.findall(r'\W', "a!@b#c$"))
# 输出:['!', '@', '#', '$']
# 12. \s 匹配空白字符
print("案例11(\s):", repr(re.search(r'\s', "空格 制表符\t").group()))
# 输出:' '
# 13. \S 匹配非空白字符
print("案例12(\S):", re.findall(r'\S', "a b\tc\n"))
# 输出:['a', 'b', 'c']
3.1.3 语法规则--限定符
限定符用于定义前面字符的重复次数。常用限定符如下: - {n}:精确匹配,精确匹配n次。 - {n,}:最少匹配,匹配至少n次。 - {n,m}:区间匹配,匹配n到m次。 - *:星号,匹配0次或多次(贪婪模式,尽可能多的匹配结果),等同于{0,}。 - +:加号,匹配1次或多次(贪婪模式,尽可能多的匹配结果),等同于{1,}。 - ?:问号,匹配0次或1次,等同于{0,1}。 - *?:非贪婪星号,最小次数匹配,匹配0次。 - +?:非贪婪加号,最小次数匹配,匹配1次。 - ??:非贪婪问号,最小次数匹配,匹配0次。
代码示例:
import re
text = "X-ABC X-ABCDE X-A X-B X-CA X-AX-AC X-AAA"
# 1. {n} 精确匹配n次
exact3 = re.findall(r'[A-Za-z]{2}', text) # 匹配恰好3位字母
print("{2} 匹配:", exact3)
# 2. {n,} 至少匹配n次
at_least2 = re.findall(r'[A-Za-z]{2,}', text) # 匹配至少2个字母
print("{2,} 匹配:", at_least2[:3])
# 3. {n,m} 区间匹配
range4_6 = re.findall(r'X-[A-Z]{2,4}', text) # 匹配2-4个大写字母
print("{2,4} 匹配:", range4_6)
# 4. * 匹配0次或多次(贪婪模式)
stars = re.findall(r'X-A*', text) # 匹配X-后有0个后者N个A
print("* 匹配:", stars)
# 5. + 匹配1次或多次(贪婪模式)
pluses = re.findall(r'X-A+', text) # 匹配X-后至少有1个A
print("+ 匹配:", pluses)
# 6. ? 匹配0次或1次
questions = re.findall(r'X-A?', text) # 匹配X-后有0个或1个A
print("? 匹配:", questions)
# 7. *? 最小次数匹配
non_greedy_star = re.findall(r'X-A*?', text) # 匹配X-后尽量少的A(0个A)
print("*? 匹配:", non_greedy_star)
# 8. +? 最小次数匹配
non_greedy_plus =re.findall(r'X-A+?', text) # 匹配X-后只有1个A
print("+? 匹配:", non_greedy_plus)
# 9. ?? 最小次数匹配(特殊用途)
optional_char = re.findall(r'X-A??', text) # 匹配X-后尽量少的A(0个A)
print("?? 匹配:", optional_char)
3.1.4 语法规则--修饰符
修饰符主要用于改变正则表达式的匹配行为。部分常用修饰符如下:
- re.IGNORECASE:可用缩写形式re.I,忽略大小写匹配。
- re. MULTILINE:可用缩写形式re.M,多行匹配(使\^/\$匹配每行的开始/结束)。
- re.DOTALL:可用缩写形式re.S,使“.”匹配包括换行在内的所有字符。
- re.VERBOSE:可用缩写形式re.X,为了增加可读性,忽略空格和#后面的注释。
代码示例:
import re
# 1.re.I使用示例
text = "Apple BANANA Cherry"
# 匹配所有水果名称(不区分大小写)
matches = re.findall(r'\bapple\b|\bbanana\b|\bcherry\b', text, flags=re.I)
print(matches) # 输出:['Apple', 'BANANA', 'Cherry']
# 2.re.M使用示例
text = """101: 项目A
202: 项目B
303: 项目C"""
# 匹配每行开头的3位数字
matches = re.findall(r'^\d{3}', text, flags=re.M)
print(matches) # 输出:['101', '202', '303']
# 3.re.S使用示例
text = """/* 注释开始
这是一个多行注释
注释结束 */"""
# 匹配多行注释内容
match = re.search(r'/\*(.*?)\*/', text, flags=re.S)
if match:
# 输出:注释开始\n这是一个多行注释\n注释结束
print(match.group(1).strip())
# 4.re.X使用示例
# 验证日期格式(YYYY-MM-DD)
pattern = '''
^ # 字符串开始
(\d{4}) # 年(4位数字)
- # 分隔符
(0[1-9]|1[0-2]) # 月(01-12)
- # 分隔符
(0[1-9]|[12]\d|3[01]) # 日(01-31)
$ # 字符串结束
'''
text = "2023-13-32" #无效日期
match = re.match(pattern,text,flags=re.X)
print(match) #输出:None
3.2 实验室预约系统
结合上面的知识点,来看一个实验室预约系统小程序。完整代码参考如下(注意:复制使用请保持缩进相同)。
import re
import sys
import os
from datetime import datetime
# ========== 正则表达式库(含标志位+分组) ==========
REGEX = {
"学号": re.compile(r"^[A-Z]{2}\d{7}$"), # PY2024001
"姓名": re.compile(r"^[\u4e00-\u9fa5·]{2,8}$"), # 张·三
"时间": re.compile(r"""
^ # 开头
(?P<date>\d{4}-\d{2}-\d{2}) # 日期
\s+ # 空格
(?P<time>\d{2}:\d{2}) # 时间
$ # 结尾
""", re.X), # 带注释的正则
"设备": re.compile(r"(电脑|投影仪|示波器)", re.I), # 忽略大小写
"理由": re.compile(r"^[^\n]{10,50}$"), # 非空,10-50字符,不含换行
}
# ========== 核心功能类 ==========
class LabSystem:
def __init__(self):
self.bookings = self._load_bookings()
def _load_bookings(self):
"""加载预约记录信息"""
bookings = []
return bookings
def _mask_id(self, stu_id):
"""正则脱敏:隐藏学号中间4位"""
return re.sub(r"(\w{2})\d{4}(\w{3})", r"\1****\2", stu_id)
def validate(self, field, value):
"""带场景的正则校验(含错误提示)"""
if field == "时间":
try:
datetime.strptime(value, "%Y-%m-%d %H:%M")
if not (9 <= int(value[11:13]) <= 17): # 9-17点可预约
return False, "仅支持9:00-17:00预约"
except:
return False, "时间格式错误(YYYY-MM-DD HH:MM)"
return True, ""
match = REGEX[field].match(value)
if not match:
return False, f"格式错误({field})"
# 设备黑名单校验(负向断言)
if field == "设备" and re.search(r"(矿机|加密狗)", value, re.I):
return False, "禁止预约敏感设备"
return True, ""
def book_lab(self):
"""互动式预约(全正则校验)"""
print("\n=== 实验室预约 ===")
booking = {}
# 学号校验(re.match+边界符)
while True:
stu_id = input("学号: ").strip().upper()
valid, msg = self.validate("学号", stu_id)
if valid:
booking["学号"] = stu_id
break
print(f"{msg}(如PY2024001)")
# 姓名校验(字符类+量词)
while True:
name = input("姓名(2-8位汉字,可含·): ").strip()
valid, msg = self.validate("姓名", name)
if valid:
booking["姓名"] = name
break
print(f"{msg}(如张·三)")
# 时间校验(分组+断言)
while True:
time_str = input("预约时间(YYYY-MM-DD HH:MM): ").strip()
valid, msg = self.validate("时间", time_str)
if valid:
booking["时间"] = time_str
break
print(f"{msg}(如2024-09-01 14:30)")
# 设备校验(枚举+re.I)
while True:
device = input("预约设备(电脑/投影仪/示波器): ").strip()
valid, msg = self.validate("设备", device)
if valid:
booking["设备"] = device
break
print(f"{msg}")
# 理由校验(非贪婪+re.S)
while True:
reason = input("预约理由(10-50字,不含换行): ").strip()
if len(reason) < 10:
print("理由过短(至少10字)")
continue
valid, msg = self.validate("理由", reason)
if valid:
booking["理由"] = reason
break
self.bookings.append(booking)
print("预约成功!记录已保存!")
def search_booking(self):
"""模糊搜索(re.I+re.M)"""
keyword = input("搜索关键词(学号/姓名/设备): ").strip()
results = []
for b in self.bookings:
# 支持学号后4位模糊匹配(分组引用)
if re.search(rf"{keyword[-4:]}", b["学号"], re.I):
results.append(b)
# 姓名拼音模糊匹配(re.I)
elif re.search(re.sub(r"[·\s]", "", keyword).lower(), b["姓名"].lower()):
results.append(b)
if not results:
print("无匹配记录(支持部分学号/拼音搜索)")
return
print("\n最近3条匹配记录:")
for i, b in enumerate(results[:3], 1):
print(f"{i}. {b['时间']} {b['设备']} - {b['姓名']}({b['学号']})")
# ========== 交互界面 ==========
def main():
system = LabSystem()
while True:
print("\n=== 实验室预约系统 ===")
print("1. 预约实验室(含正则校验)")
print("2. 搜索预约记录(模糊匹配)")
print("3. 查看所有记录(脱敏显示)")
print("4. 退出系统")
choice = input("请选择操作(1-4): ").strip()
if choice == "1":
system.book_lab()
elif choice == "2":
system.search_booking()
elif choice == "3":
system._load_bookings() # 刷新显示
print("\n脱敏记录(最近5条):")
for b in system.bookings[-5:]:
print(f"- {b['时间']} {b['设备']}: {b['姓名']} ({b['学号']})")
elif choice == "4":
print("\n系统退出,数据已保存(含正则脱敏)")
sys.exit(0)
else:
print("无效选择,请输入1-4")
if __name__ == "__main__":
main()
同时,可以参考上面代码进行扩展练习,例如:从学号自动生成邮箱并使用正则校验邮箱格式。
6万+

被折叠的 条评论
为什么被折叠?



