Python 正则表达式(十四):文本匹配、查找与替换

发布时间:2026/9/30 2:46:33

Python 正则表达式(十四):文本匹配、查找与替换 相信大家平时都见过这样的内容内容里明明有我们需要的信息但它们都混杂在一大段文字里。比如日志中的时间和 IP聊天记录里的手机号和邮箱没法像 JSON 或 CSV 那样直接按字段读取。思维导图这种时候正则表达式就很适合用来处理文本匹配。今天我们从第一次匹配开始逐步讲清楚查找、分组、替换和拆分最后再写一个日志解析脚本。一、正则表达式是什么正则表达式可以理解成一套文本规则。我们先写出目标内容的特征再让程序按这套规则去查找。例如下面这段文字中包含一个订单号text订单号A20260919状态已发货如果订单号始终由一个大写字母和八位数字组成它的规则可以写成[A-Z]\d{8}其中[A-Z]代表一位大写字母\d{8}代表连续八位数字。正则表达式本身并不负责读取文件它处理的是字符串。日志文件、网页内容或者 CSV 字段读成字符串以后都能交给它匹配。二、使用 re.search() 完成第一次匹配Python 的正则功能放在标准库re中不需要另外安装。先用search()找出订单号中的数字importre text订单号A20260919resultre.search(r\d,text)print(result)\d表示数字后面的表示前一个规则可以出现一次或多次。运行以后会得到一个 Match 对象re.Match object; span(5, 13), match20260919Match 对象里保存了匹配内容和位置。一般会通过这些方法读取print(result.group())print(result.start())print(result.end())print(result.span())输出结果20260919 5 13 (5, 13)start()是起始下标end()是结束位置span()会把两者放进一个元组。没有找到内容时search()返回None。直接调用group()会报错实际使用时要先判断resultre.search(r\d,订单暂未生成)ifresult:print(result.group())else:print(没有找到订单号)三、为什么正则前面经常带 r正则表达式中经常出现反斜杠。Python 字符串也使用反斜杠表示转义字符例如\n是换行\t是制表符。两套规则写在一起很容易把反斜杠写乱。在字符串前加r可以创建原始字符串patternr\d这里的r只影响 Python 解析字符串的方式最终交给正则引擎的规则还是\d。写正则时建议默认使用原始字符串。需要匹配反斜杠本身时正则中仍然要写两个反斜杠textrC:\Users\ivan\notes.txtresultre.search(r\\Users\\,text)print(result.group())四、search、match 和 fullmatch 有什么区别这三个方法都会返回 Match 对象区别在于从哪里开始找以及需要匹配多少内容。search()会在整个字符串中查找第一个符合规则的位置importre text用户ID: 1024resultre.search(r\d,text)print(result.group())match()只检查字符串开头print(re.match(r用户,text))print(re.match(r\d,text))第二次返回None因为数字不在开头。fullmatch()要求整段字符串都符合规则很适合校验格式order_idA20260919ifre.fullmatch(r[A-Z]\d{8},order_id):print(订单号格式正确)else:print(订单号格式错误)查找一段内容时用search()检查开头时用match()校验完整格式时用fullmatch()。五、常用字符规则正则中最常见的几个规则如下规则含义示例\d数字0到9\D非数字字母、中文、符号\wUnicode 字母、数字和下划线user_01、中文\W不属于\w的字符空格和多数标点\s空白字符空格、制表符、换行\S非空白字符普通可见文字.除换行以外的任意字符字母、数字、标点下面从一段文字中找出价格text键盘价格 299 元鼠标价格 129 元pricesre.findall(r\d,text)print(prices)输出结果[299,129]\w在 Python 3 中默认能匹配中文。如果只想匹配英文字母、数字和下划线可以明确写成[A-Za-z0-9_]不要把两者当成完全相同的规则。六、使用字符集合限制范围方括号可以列出允许出现的字符text订单 A102、B205、c308ordersre.findall(r[A-Z]\d,text)print(orders)输出[A102,B205][A-Z]只匹配大写英文字母小写的c308不符合规则。常见写法还有[abc] 匹配 a、b 或 c [0-9] 匹配一位数字 [a-zA-Z] 匹配一位英文字母 [^0-9] 匹配非数字字符方括号开头的^表示排除。它放在方括号外时含义会变成字符串开头后面会单独讲。七、数量词控制出现次数字符规则只说明“匹配什么”数量词负责说明“出现几次”。数量词含义*零次或多次一次或多次?零次或一次{m}恰好 m 次{m,n}最少 m 次最多 n 次{m,}最少 m 次使用数量词检查手机号的基本结构phone13812345678resultre.fullmatch(r1[3-9]\d{9},phone)print(bool(result))1是固定开头[3-9]匹配第二位\d{9}要求后面还有九位数字。这里检查的是常见手机号格式不能证明号码真实存在。八、使用锚点限制开头和结尾^表示字符串开头$表示字符串结尾textERROR: 登录失败print(bool(re.search(r^ERROR,text)))print(bool(re.search(r失败$,text)))两次结果都是True。锚点匹配的是位置本身不会占用字符。校验一整段内容时也可以直接使用fullmatch()usernameivan_2026ifre.fullmatch(r[A-Za-z][A-Za-z0-9_]{3,15},username):print(用户名可用)这个规则要求用户名以英文字母开头总长度为 4 到 16 位后面可以使用字母、数字和下划线。九、分组一次取出多个字段圆括号可以把一段规则放进一个分组。下面从日期中分别取出年、月、日importre date_text2026-09-19resultre.fullmatch(r(\d{4})-(\d{2})-(\d{2}),date_text,)ifresult:print(result.group(0))print(result.group(1))print(result.group(2))print(result.group(3))print(result.groups())group(0)是完整匹配后面的编号按左括号出现的顺序排列。groups()会一次返回所有分组2026-09-19 2026 09 19 (2026, 09, 19)分组多起来以后记编号很不方便。这时可以给分组命名pattern(r(?Pyear\d{4})-r(?Pmonth\d{2})-r(?Pday\d{2}))resultre.fullmatch(pattern,date_text)ifresult:print(result.group(year))print(result.groupdict())groupdict()会把命名分组整理成字典{year:2026,month:09,day:19}解析日志、订单和接口返回文本时命名分组比数字编号更容易维护。十、只分组不保存内容有时圆括号只是为了把几条规则放在一起并不需要把内容保存成分组。可以使用(?:...)text图片cover.jpg附件report.pdffilesre.findall(r\w\.(?:jpg|png|pdf),text)print(files)输出[cover.jpg,report.pdf]如果写成(jpg|png|pdf)findall()会优先返回捕获分组里的扩展名。这里使用非捕获分组得到的才是完整文件名。竖线|表示多个规则任选一个INFO|WARNING|ERROR它能匹配三种日志级别中的任意一种。十一、findall 和 finditer 批量查找search()只返回第一个匹配。想拿到所有结果可以使用findall()text今天处理 A102明天处理 A205A308 已完成ordersre.findall(r[A-Z]\d,text)print(orders)输出[A102,A205,A308]还需要每个结果的位置时使用finditer()formatchinre.finditer(r[A-Z]\d,text):print(match.group(),match.start(),match.end(),)finditer()返回一个迭代器不会提前把所有 Match 对象放进列表。数据比较多或者需要逐个查看分组和位置时它更合适。十二、贪婪匹配和非贪婪匹配数量词默认会尽量多匹配。下面这段 HTML 中有两个标签htmlpPython/pp正则表达式/pprint(re.findall(rp.*/p,html)).*会从第一个p一直匹配到最后一个/p[pPython/pp正则表达式/p]在数量词后面加?可以改成非贪婪匹配print(re.findall(rp.*?/p,html))这次得到两个结果[pPython/p,p正则表达式/p]这个例子适合说明贪婪规则真实 HTML 的结构远比它复杂。需要可靠地解析网页时应该使用 HTML 解析库不要只靠一条正则处理整份页面。十三、使用 re.sub() 替换内容re.sub()会把符合规则的内容替换掉。处理手机号脱敏时可以保留前三位和后四位importre phone13812345678maskedre.sub(r(\d{3})\d{4}(\d{4}),r\1****\2,phone,)print(masked)输出138****5678替换字符串中的\1和\2引用了第一个、第二个分组。分组较多时可以改用命名分组patternr(?Pstart\d{3})\d{4}(?Pend\d{4})maskedre.sub(pattern,r\gstart****\gend,phone,)替换逻辑需要计算时可以把函数传给sub()text商品价格 299 元优惠价 199 元defadd_currency(match):pricematch.group()returnf¥{price}resultre.sub(r\d,add_currency,text)print(result)十四、使用 re.split() 拆分内容普通字符串的split()只能方便地处理一种固定分隔符。文本中混用了逗号、分号和空格时可以使用re.split()textPython,Java;Go Rustlanguagesre.split(r[,;\s],text)print(languages)输出[Python,Java,Go,Rust][,;\s]表示一个或多个逗号、分号或空白字符。实际数据结尾可能还带分隔符可以顺手去掉空字符串languages[itemforiteminre.split(r[,;\s],text)ifitem]十五、重复使用规则时先编译同一条规则需要使用多次时可以用re.compile()创建正则对象importre order_patternre.compile(r[A-Z]\d{3})texts[新订单 A102 已创建,B205 等待付款,没有订单号,]fortextintexts:resultorder_pattern.search(text)ifresult:print(result.group())编译后的对象也有search()、findall()、sub()等方法。这样写能把规则集中放在一个位置名称也能说明它是用来匹配什么的。十六、常用匹配标志匹配标志可以改变正则的工作方式。忽略英文字母大小写时使用re.IGNORECASEtextError: file not foundresultre.search(rerror,text,flagsre.IGNORECASE,)print(result.group())处理多行文本时re.MULTILINE会让^和$匹配每一行的开头和结尾logsINFO: 启动成功 ERROR: 数据库连接失败 INFO: 开始重试errorsre.findall(r^ERROR:.*$,logs,flagsre.MULTILINE,)print(errors).默认不匹配换行。需要让它跨行匹配时可以使用re.DOTALL。多个标志能用|组合flagsre.IGNORECASE|re.MULTILINE十七、写正则时容易踩的坑最常见的问题是规则写得太宽。比如.*什么都能接短文本里看不出问题换一批数据就可能吞掉多余内容。能写清字符范围时尽量使用[A-Z]、\d{4}这类具体规则。第二个问题是忘记处理None。search()和fullmatch()都有匹配失败的时候调用group()以前先判断结果。还要留意捕获分组对findall()返回值的影响textA102 B205print(re.findall(r[A-Z]\d,text))print(re.findall(r([A-Z])\d,text))第二条规则包含捕获分组结果会变成[A, B]。如果括号只用来控制范围改成(?:[A-Z])。正则也不适合承担所有格式校验。邮箱地址、URL 和 HTML 都有许多边界情况。教程里常见的邮箱规则可以拿来提取简单地址不能当作完整标准的验证器。十八、综合示例解析日志并导出 CSV现在把正则和上一章的 CSV 连起来。我们准备一份app.log2026-09-19 10:15:32 INFO userivan actionlogin ip192.168.1.10 2026-09-19 10:16:03 WARNING useralice actiondownload ip10.0.0.5 这是一行无法识别的内容 2026-09-19 10:18:45 ERROR userbob actionupload ip172.16.3.8项目结构如下regex_demo/ ├─ parse_log.py ├─ data/ │ └─ app.log └─ output/在parse_log.py中写入frompathlibimportPathimportcsvimportre base_dirPath(__file__).resolve().parent log_filebase_dir/data/app.logoutput_dirbase_dir/outputcsv_fileoutput_dir/log_report.csvoutput_dir.mkdir(parentsTrue,exist_okTrue)log_patternre.compile(r^(?Ptime\d{4}-\d{2}-\d{2} r\d{2}:\d{2}:\d{2})\sr(?PlevelINFO|WARNING|ERROR)\sruser(?Puser\w)\sraction(?Paction\w)\srip(?Pip(?:\d{1,3}\.){3}\d{1,3})$)records[]failed_lines[]withlog_file.open(r,encodingutf-8)asfile:forline_number,lineinenumerate(file,start1):lineline.strip()ifnotline:continueresultlog_pattern.fullmatch(line)ifresult:records.append(result.groupdict())else:failed_lines.append((line_number,line))fieldnames[time,level,user,action,ip,]withcsv_file.open(w,encodingutf-8-sig,newline,)asfile:writercsv.DictWriter(file,fieldnamesfieldnames,)writer.writeheader()writer.writerows(records)print(f成功解析{len(records)}行)print(f解析失败{len(failed_lines)}行)print(fCSV 文件{csv_file})forline_number,contentinfailed_lines:print(f第{line_number}行无法识别{content})这里使用命名分组直接生成字典再交给csv.DictWriter()写入文件。无法匹配的行没有被悄悄丢掉程序会保存行号和原内容方便回头检查规则或原始数据。IP 的规则(?:\d{1,3}\.){3}\d{1,3}只检查“由四段一到三位数字组成”的外形没有继续判断每一段是否处于0到255。需要严格验证 IP 时可以把匹配结果交给标准库ipaddress再检查。生成的log_report.csv如下time,level,user,action,ip 2026-09-19 10:15:32,INFO,ivan,login,192.168.1.10 2026-09-19 10:16:03,WARNING,alice,download,10.0.0.5 2026-09-19 10:18:45,ERROR,bob,upload,172.16.3.8!10-log-pipeline.png十九、练习练习一提取所有价格准备字符串键盘 299 元鼠标 129 元显示器 1599 元使用正则提取所有价格并把结果转换成整数列表。练习二检查订单号订单号由两个大写字母和六位数字组成。使用fullmatch()分别检查AB123456、A123456和ab123456。练习三提取邮箱从一段文本中提取形如nameexample.com的简单邮箱地址。先处理常见英文字母、数字、点、下划线和连字符不需要覆盖所有邮箱标准。练习四隐藏身份证号把十八位身份证号的中间八位替换成星号只保留前六位和后四位。使用捕获分组保留两端内容。练习五解析文件名从report_2026-09-19.csv中取出文件类型和日期分别保存到命名分组date和extension中。练习六统计错误日志读取一份日志文件使用re.MULTILINE找出所有以ERROR开头的行。再从每一行中提取时间和错误内容并写入 CSV 文件。学正则时没必要先背完所有符号。先把目标文本找出来标清哪些内容固定、哪些内容会变再把它们换成字符规则和数量词。规则写完后多准备几条正常数据和异常数据测试比盯着一条样例反复修改更容易发现问题。
延伸阅读

更多相关文章

2026/9/30 2:46:33

Spring AI 接入 DeepSeek Chat 模型

本文介绍如何在 Spring Boot 项目中使用 Spring AI 接入 DeepSeek Chat 模型,实现一个简单的对话接口。示例包含 Maven 依赖、application.yml 配置、ChatClient 调用代码以及常见问题排查。示例基于当前 Spring AI 2.0.x 的自动配置方式。Spring AI 和 DeepSeek 的…

2026/9/30 2:46:33

SPFA 算法简介及经典实例

● SPFA 算法 (1)SPFA 算法,即最短路径快速算法,是基于 Bellman-Ford 算法优化而来的单源最短路径算法,适用于带负权边、无负权环的有向图或无向图,在算法竞赛中应用广泛。 (2)SPFA …

2026/9/30 2:46:33

GCC 参数记不住?这份语法速查 + 多参数组合示例,收藏就够

代码写得没问题,一编译却蹦出一堆 undefined reference to xxx。改了半天,最后发现是命令里库的顺序写反了。今天把 GCC 语法、核心参数、多参数组合,以及最容易踩的链接顺序坑,一篇讲清。 一、gcc 基本语法 一条 gcc 命令长这样…

2026/9/30 3:51:37

Model-Optimizer:大模型推理全链路分层优化实践指南

1. 项目概述:Model-Optimizer不是工具名,而是一类工程实践的统称 “Model-Optimizer”这个词在当前技术社区里常被误认为是一个具体软件或开源项目——比如有人搜“Model-Optimizer下载”“Model-Optimizer GitHub”,结果却找不到官方仓库。…

2026/9/30 3:51:37

SFINAE 机制全解析:从 enable_if 到 void_t 的模板实战

有些话说在前头:模板编程里最让人上头的部分,多半不是怎么把类型算出来,而是怎么让编译器“在你不想要的时候别报错,在你想选的时候刚好选对”。SFINAE 就是这一整套把戏的核心发动机。如果你玩 C 模板玩到一定阶段,一…

2026/9/30 3:51:37

大模型推理加速工程实践:TensorRT与vLLM协同部署指南

1. 项目概述:Model-Optimizer不是工具名,而是工程范式的代号“Model-Optimizer”这个标题乍看像某个开源库或GUI软件的名称,但结合NVIDIA、TensorRT-LLM、vLLM、PT文件转换、Docker镜像部署等高频热词,它实际指向的是一整套面向生…

2026/9/30 3:51:37

TensorFlow 2024实战:安装避坑、Keras 3与模型部署全流程

2024年聊起TensorFlow,注定不是一句"PyTorch已经赢了"能概括的。过去一年我在好几个生产项目里来回切换框架,发现一个特别有意思的错位:社交媒体上讨论最多的永远是PyTorch生态、LoRA微调、扩散模型,可当我拉开服务器上…

2026/9/30 3:51:37

自动化脚本实战指南:从Shell到UI、接口与CI部署

这些搜索词扔给我的一瞬间,我基本就明白了——问“自动化与脚本”的人,十个里有八个不是想听理论,而是正被某个具体的活儿追着跑:要么是测试任务重复到吐,要么是部署流程卡得人想骂人,再要么就是成天在不同…

2026/9/30 3:46:37

模型优化实战:从ONNX到TensorRT的量化、剪枝与部署加速指南

算力需求暴涨、落地 deadline 卡得死死的——“模型是训出来了,但根本跑不动”这种话我这两年听了太多。模型结构和训练效果只是第一步,真正考验工程能力的,是把一个训练好的模型安全、高效、低损耗地塞进生产环境。就在这个环节,…

2026/9/29 11:07:23

东莞市品牌网站建设报价常见报错与解决

东莞品牌网站建设报价单背后:一份保姆级建站教程避坑实录 网站做好了没人访问,这大概是很多老板最头疼的事。花了大几万做的品牌站,上线后流量惨淡,比路边摊还冷清。别急着骂外包公司,很多“东莞品牌网站建设报价”里藏着不少猫腻,比如用模板站冒充定制…

2026/9/29 21:48:03

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/9/29 7:00:49

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/9/30 0:01:22

MATLAB+Yalmip+CPLEX实战:综合能源系统优化调度全流程解析

做综合能源系统优化调度这活儿,最痛苦的不是建模本身,而是模型写完之后不知道该怎么求解。看论文里轻飘飘一句“采用Yalmip调用CPLEX求解”,自己上手时却往往卡在环境配置、变量声明、约束写法和求解状态判读上,一耗就是两三天。这…

2026/9/30 0:01:22

I3C比I2C快10倍?RK3576实战:速率、DTS配置与混合总线避坑指南

I3C 比 I2C 快 10 倍?这句话在嵌入式群里传了很久,每次都能吵出一堆截图。前段时间我正好在 RK3576 上调板级 I3C 接口,从控制器寄存器一路摸到 Linux DTS 配置,踩了不少坑,也把这笔速度账彻底算明白了。本文就用 RK35…

2026/9/30 0:01:22

字符串转对象:JSON.parse、new Function与URLSearchParams

“字符串转对象”这几个字,我在技术群里见过的问法至少有十几种:有人拿着一串{a:1,b:2}说 JSON.parse 直接报错,有人要从 URL 里抠出参数,还有人只是想把abc变成能挂属性的东西。js 这门语言里,字符串和对象之间的转换…

2026/9/29 3:53:39

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/29 9:46:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/29 6:36:14

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑