Python进阶系列之-正则表达式
Python进阶系列之-正则表达式写在前面在日常开发中我们经常需要处理字符串——校验邮箱格式、提取手机号、过滤敏感词、解析HTML标签……如果用传统字符串方法往往需要写大量冗长的if-else和循环不仅效率低下而且容易出错。正则表达式Regular Expression正是解决这类问题的利器它用一套简洁的规则实现对字符串的精准匹配、查找和替换是每个 Python 开发者必须掌握的技能。本文从正则表达式的核心概念出发系统讲解 Python 中re模块的常用函数match、search、compile、sub深入解析各类元字符、数量词、边界符、分组与反向引用最后通过多个实战案例邮箱校验、HTML标签匹配、敏感词过滤带你彻底掌握正则表达式。文章目录Python进阶系列之-正则表达式一、正则表达式基础1.1 什么是正则表达式1.2 正则表达式在 Python 中的使用步骤1.3 三个核心函数二、正则表达式核心规则2.1 匹配单个字符2.2 匹配多个字符数量词贪婪 vs 非贪婪经典匹配陷阱2.3 边界匹配2.4 选择与分组三、re 模块进阶用法3.1 re.compile() 编译正则3.2 re.sub() 替换字符串3.3 标志位flags3.4 re.split()按正则规则分割字符串四、综合实战案例4.1 邮箱校验4.2 手机号提取4.3 敏感词过滤4.4 提取 URL 中的域名4.5 HTML 标签合法性校验五、常见误区与注意事项六、正则表达式速查表常用正则开箱即用速查七、全文总结一、正则表达式基础1.1 什么是正则表达式正则表达式Regular Expression简称 regex是一种描述字符串模式的表达式。它用特定的符号组合来定义一组字符串的规则然后利用这些规则对目标字符串进行匹配、查找、替换等操作。核心价值从海量文本中快速提取目标信息验证用户输入的合法性邮箱、手机号、密码强度批量替换符合某种模式的文本1.2 正则表达式在 Python 中的使用步骤Python 通过内置的re模块提供正则支持使用流程非常简单importre# 1. 定义正则规则模式patternr\d# 匹配一个或多个数字# 2. 调用 re 模块的函数进行匹配resultre.match(pattern,123abc)# 3. 获取匹配结果ifresult:print(result.group())# 输出: 123注意正则表达式字符串前建议加上rraw string表示原始字符串避免反斜杠\被 Python 解释器转义。1.3 三个核心函数函数作用匹配方式re.match(pattern, string)从字符串的起始位置开始匹配必须从头匹配不匹配则返回 Nonere.search(pattern, string)在整个字符串中查找第一个匹配项可以从任意位置开始匹配re.findall(pattern, string)查找字符串中所有匹配项返回列表全局搜索示例对比importre texthello 123 world 456# match: 从头匹配print(re.match(r\d,text))# None因为开头是字母# search: 查找第一个print(re.search(r\d,text).group())# 123# findall: 查找所有print(re.findall(r\d,text))# [123, 456] 快速记忆口诀match从头开始匹配开头不对直接失败search全文扫描查找找到第一个就返回findall全文全部找完返回列表形式二、正则表达式核心规则2.1 匹配单个字符元字符含义等价表示.匹配任意一个字符除换行符\n-\d匹配任意一个数字[0-9]\D匹配任意一个非数字[^0-9]\s匹配任意一个空白字符空格、\t、\n、\r-\S匹配任意一个非空白字符-\w匹配任意一个单词字符字母、数字、下划线、汉字[a-zA-Z0-9_\u4e00-\u9fa5]\W匹配任意一个非单词字符-[abc]匹配a、b、c中的任意一个-[^abc]匹配除了a、b、c以外的任意一个-示例importre# . 匹配任意一个字符print(re.match(r.it.,.ita).group())# .ita# \d 匹配数字print(re.match(r\d{3},123abc).group())# 123# [abc] 匹配 a、b、c 之一print(re.match(r[abc]\d,aaa123).group())# aaa12.2 匹配多个字符数量词数量词含义举例?前面的字符出现0 次或 1 次colou?r可匹配color或colour前面的字符出现1 次或多次\d匹配一个或多个数字*前面的字符出现0 次或多次\d*匹配零个或多个数字{n}前面的字符出现恰好 n 次\d{3}匹配三位数字{n,}前面的字符出现至少 n 次\d{3,}匹配三位及以上数字{n,m}前面的字符出现n 到 m 次\d{2,5}匹配二到五位数字示例importre# ? 表示0或1次print(re.match(rcolou?r,color).group())# colorprint(re.match(rcolou?r,colour).group())# colour# 表示1次或多次print(re.match(r\d,123abc).group())# 123# * 表示0次或多次print(re.match(rab*,a).group())# aprint(re.match(rab*,abbb).group())# abbb# {n,m} 指定次数范围print(re.match(r\d{2,4},12345).group())# 1234贪婪匹配尽可能多贪婪 vs 非贪婪经典匹配陷阱默认情况下*、、{n,m}都是贪婪模式会尽可能多地匹配字符直到无法继续为止。在数量词后加?可切换为非贪婪懒惰模式尽可能少地匹配满足条件就停止。最典型的场景是匹配 HTML 标签importre textdiv第一段/divdiv第二段/div# 贪婪模式匹配到最后一个 /div 才停止greedyre.match(rdiv.*/div,text)print(greedy.group())# 输出div第一段/divdiv第二段/div# 非贪婪模式遇到第一个 /div 就停止lazyre.match(rdiv.*?/div,text)print(lazy.group())# 输出div第一段/div记忆技巧加?就是“别贪了够了就停”。2.3 边界匹配元字符含义^匹配字符串的开头$匹配字符串的结尾\b匹配单词的边界示例importre# ^ 开头匹配print(re.search(r^\d,123abc).group())# 123print(re.search(r^\d,a123))# None# $ 结尾匹配print(re.search(r\d$,abc123).group())# 123print(re.search(r\d$,123abc))# None# 同时使用 ^ 和 $ 进行全匹配print(re.match(r^\d{3}$,123).group())# 123print(re.match(r^\d{3}$,1234))# None2.4 选择与分组元字符含义|或关系匹配左边或右边的表达式()分组将括号内的内容作为一个整体并可以捕获匹配结果\num反向引用引用第 num 个分组匹配到的内容(?Pname)给分组命名(?Pname)引用命名分组匹配到的内容示例使用分组提取信息importre# 提取 QQ 号和号码resultre.match(r(qq):(\d{5,11}),qq:12306)ifresult:print(result.group(0))# qq:12306print(result.group(1))# qqprint(result.group(2))# 12306示例反向引用校验 HTML 标签importre# 匹配单级标签确保前后标签一致resultre.match(r([a-zA-Z]).*/\1,htmlhello/html)print(result.group())# htmlhello/html# 匹配多级嵌套标签resultre.match(r([a-zA-Z])([a-zA-Z]).*/\2/\1,divspantext/span/div)print(result.group())# divspantext/span/div示例命名分组resultre.match(r(?Ptag\w).*/(?Ptag),pcontent/p)print(result.group())# pcontent/p三、re 模块进阶用法3.1re.compile()编译正则当需要多次使用同一个正则表达式时可以先编译成正则对象提高匹配效率。importre# 编译正则patternre.compile(r\d)# 使用编译后的对象进行匹配print(pattern.findall(abc123def456))# [123, 456]print(pattern.search(abc123).group())# 1233.2re.sub()替换字符串re.sub()用于替换字符串中符合正则的部分功能比字符串的replace()强大得多。importre# 将敏感词替换为 *text车主说: 你的刹车片应该换了啊, 嘿嘿patternr啊|阿|嘿|呵|哈|啦|嘻|桀resultre.sub(pattern,*,text)print(result)# 车主说: 你的刹车片应该换了*, 嘿*# 支持回调函数defrepl(match):return**len(match.group())resultre.sub(r\d,repl,我的电话是123456789)print(result)# 我的电话是*********3.3 标志位flagsre模块支持多种标志位用于改变匹配行为标志缩写作用re.IIGNORECASE忽略大小写re.MMULTILINE多行模式^和$匹配每行的开头和结尾re.SDOTALL让.匹配包括换行符在内的任意字符importre# 忽略大小写print(re.match(rhello,HELLO,re.I).group())# HELLO# 多行模式textfirst line\nsecond lineprint(re.findall(r^\w,text,re.M))# [first, second]# re.S 让 . 匹配换行符实现跨行匹配textdiv 第一行内容 第二行内容 /div# 默认 . 不匹配换行匹配失败print(re.search(rdiv.*/div,text))# None# 加 re.S 后成功匹配跨行内容resultre.search(rdiv.*?/div,text,re.S)print(result.group())# 完整输出包含换行的div标签内容3.4re.split()按正则规则分割字符串字符串原生的split()只能按固定字符分割re.split()支持按正则规则分割能同时处理多种分隔符。importre# 按逗号、分号、空格分割字符串textapple,banana; orange graperesultre.split(r[,; ],text)print(result)# 输出[apple, banana, orange, grape]四、综合实战案例4.1 邮箱校验importredefvalidate_email(email):patternr^[a-zA-Z0-9_]{4,20}(163|126|qq|gmail)\.(com|cn)$ifre.match(pattern,email):returnTruereturnFalse# 测试emails[hello163.com,testqq.cn,bademail,toolongusername163.com]foreinemails:print(f{e}:{有效ifvalidate_email(e)else无效})4.2 手机号提取importre text我的手机是13800138000他的手机是13912345678patternr1[3-9]\d{9}phonesre.findall(pattern,text)print(phones)# [13800138000, 13912345678]4.3 敏感词过滤importredeffilter_sensitive(text,sensitive_words):# 将敏感词列表拼接成正则pattern|.join(sensitive_words)returnre.sub(pattern,***,text)words[暴力,色情,赌博]text这是一个包含暴力和色情的文本请勿赌博。print(filter_sensitive(text,words))# 这是一个包含***和***的文本请勿***。4.4 提取 URL 中的域名importre urlhttps://www.baidu.com/s?wdpythonpatternrhttps?://([^/])resultre.search(pattern,url)ifresult:print(result.group(1))# www.baidu.com4.5 HTML 标签合法性校验利用分组反向引用可以校验 HTML 标签是否成对闭合避免出现div/span这种标签不匹配的错误。importre# 案例1校验单级标签普通反向引用pattern1r([a-zA-Z]{1,6}).*/\1print(re.match(pattern1,p你好/p).group())# 匹配成功print(re.match(pattern1,p你好/div))# None标签不匹配# 案例2校验多级嵌套标签命名分组写法pattern2r(?Pouter[a-z])(?Pinnerh[1-6]).*/(?Pinner)/(?Pouter)htmldivh3标题/h3/divprint(re.match(pattern2,html).group())# 匹配成功五、常见误区与注意事项忘记加r前缀\d在普通字符串中会被转义为d导致匹配失败。始终使用r\d。混淆match和searchmatch必须从头匹配search可以匹配任意位置。贪婪匹配陷阱默认*和是贪婪的如果需要最小匹配加?。分组编号从 1 开始group(0)是整个匹配group(1)是第一个分组。正则性能问题过于复杂的正则可能导致回溯爆炸建议拆分成多个简单正则或使用re.compile预编译。特殊字符转义坑正则里. * ? | () [] {} ^ $ \都是有特殊含义的元字符如果要匹配它们本身必须加反斜杠转义。importre# 匹配小数点必须转义print(re.match(r3\.14,3.14).group())# 正确print(re.match(r3.14,3x14).group())# 也能匹配因为.代表任意字符# 匹配反斜杠需要写两次原生字符串下print(re.match(r\\,\\).group())# 匹配单个\记忆元字符想当普通字符用前面加\就对了。不要滥用正则正则不是万能的。解析 HTML/XML 结构用 BeautifulSoup、lxml 等专门库正则无法完美处理嵌套结构解析 JSON 数据用内置json模块比正则更稳健简单固定字符串替换直接用str.replace()性能比正则更好正则的定位是处理符合某种模式的纯文本复杂结构化数据优先用专用工具。六、正则表达式速查表类别符号说明字符.任意字符除\n字符集[abc]a、b、c 之一否定字符集[^abc]非 a、b、c数字\d数字[0-9]非数字\D[^0-9]空白\s空格、\t、\n、\r非空白\S非空白单词字符\w字母、数字、下划线、汉字非单词字符\W特殊字符数量?0 或 1 次数量1 次或多次数量*0 次或多次数量{n}恰好 n 次数量{n,}至少 n 次数量{n,m}n 到 m 次边界^开头边界$结尾边界\b单词边界选择分组()捕获分组反向引用\1引用第 1 个分组命名分组(?Pname)命名引用命名(?Pname)引用命名分组常用正则开箱即用速查整理了开发中最高频的正则规则收藏后可以直接调用场景正则表达式说明手机号1[3-9]\d{9}中国大陆11位手机号邮箱[a-zA-Z0-9_-][a-zA-Z0-9_-](\.[a-zA-Z0-9_-])通用邮箱格式身份证号\d{17}[\dXx]18位身份证号中文汉字[\u4e00-\u9fa5]匹配纯中文URL地址https?://[\w\-](\.[\w\-])[/#?]?.*匹配http/https链接IPv4地址\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}基础IP格式校验正整数[1-9]\d*大于0的整数七、全文总结正则表达式是用特定符号描述字符串模式的工具广泛应用于校验、查找、替换。Python 的re模块提供了match、search、findall、sub、compile、split等核心函数。元字符包括字符类、数量词、边界符、分组与反向引用掌握它们就能写出大部分正则。实战案例邮箱校验、手机号提取、敏感词过滤、URL 解析、HTML标签校验等都是日常开发的常用场景。最佳实践使用r前缀、预编译正则、注意贪婪/懒惰、合理使用分组避免滥用正则。正则表达式是程序员的瑞士军刀初学时可能觉得晦涩难懂但只要多写多用很快就能得心应手。希望这篇博客能帮你打开正则的大门从此面对字符串处理游刃有余。如果觉得有帮助欢迎点赞收藏持续更新中