Python高阶:“咬文嚼字” - 正则表达式

1 概述

1.1 背景介绍

‌Python的正则表达式‌是一种强大的文本处理工具,它使用一种特殊的字符串模式来描述、匹配和查找一系列符合某个句法规则的字符串。在Python中,我们可以通过re模块来操作正则表达式。

通过本案例可以帮助我们快速掌握Python正则表达式相关运用。

1.2 适用对象

  • 个人开发者
  • 高校学生

1.3 案例时间

本案例总时长预计30分钟。

1.4 案例流程

9fd67b06a3a809366e78c96eeba1a887.png{{{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”。

974679cf89db34fb55c1442a30839207.png

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

21f91a63b7e18ebfad752ed901c2ca6c.png

2.2 使用CodeArts IDE创建工程

参考“初识云主机:CodeArts IDE入门”案例介绍“3.1 新建工程”章节新建工程。

新建的工程包含如下三个目录文件部分:

目录文件说明
.artsCodeArts的配置文件
venv虚拟环境
main.py默认生成的入口文件

68d46cce9998a4909cd7a04f1fa7956f.png

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

同时,可以参考上面代码进行扩展练习,例如:从学号自动生成邮箱并使用正则校验邮箱格式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值