算法精讲--正则表达式(二):分组、引用与高级匹配技术

算法精讲–正则表达式(二):分组、引用与高级匹配技术 🚀

正则表达式的真正力量在于组合使用各种语法元素,创造出强大而精确的匹配模式!

—— 作者:无限大

推荐阅读时间:25 分钟

适用人群:已掌握基础正则语法,希望提升实战能力的开发者

引言:从基础到组合的飞跃 🚀

在《算法精讲–正则表达式(一)》中,我们学习了正则表达式的基础知识:字符匹配、量词和位置匹配。这些是构建正则表达式的基石,但真正让正则表达式发挥强大威力的是组合使用这些基础元素。

想象一下,基础语法就像字母表中的字母,而组合技巧则是将这些字母组成单词、句子和段落的语法规则。只有掌握了组合技巧,你才能真正用正则表达式解决复杂的文本处理问题。

本章将带你探索正则表达式的组合艺术–分组与引用,让你从正则表达式的初学者蜕变为匹配大师!


分组与引用:结构化匹配的艺术 🔄

1.1 什么是分组?为什么需要分组?

分组(Grouping) 是正则表达式中用于将多个字符或子表达式组合为一个逻辑单元的机制,通过圆括号 () 实现。分组的主要作用包括:

  1. 逻辑组合:将多个元素视为一个整体进行操作(如应用量词)
  2. 捕获数据:提取匹配结果中的特定部分
  3. 模式分支:通过 | 实现多个可选模式
  4. 引用匹配:在表达式内部或替换操作中引用已匹配的内容

没有分组,正则表达式将无法处理复杂的结构化文本匹配。想象一下,如果不能将 ab 组合为一个整体,我们将无法匹配重复出现的 ab 序列。


1.2 分组的类型

1.2.1 捕获组:提取匹配的子串 🎯

捕获组(Capturing Group) 是正则表达式中最强大的组合工具之一,它允许你将匹配的部分内容保存到临时变量中,以便后续使用。使用圆括号 () 可以创建捕获组。

基本语法与工作原理
(表达式)例:ab+c,只能匹配abc、abbc、ababcb等(ab)+c,可以匹配abc、ababc、abababc等
实用示例

示例 1:提取日期中的年、月、日

// 匹配格式为 yyyy-mm-dd 的日期
const dateRegex = /(d{4})-(d{2})-(d{2})/;
const date = "2023-12-25";
const result = date.match(dateRegex);console.log(result[0]); // 输出: 2023-12-25 (整个匹配)
console.log(result[1]); // 输出: 2023 (第一个捕获组: 年)
console.log(result[2]); // 输出: 12 (第二个捕获组: 月)
console.log(result[3]); // 输出: 25 (第三个捕获组: 日)

示例 2:交换姓名顺序

import re# 将 "姓, 名" 格式转换为 "名 姓"
name = "Doe, John"
# 使用 1 和 2 引用第一个和第二个捕获组
formatted_name = re.sub(r'(w+), (w+)', r'2 1', name)print(formatted_name)  # 输出: John Doe

1.2.2 非捕获组:提高性能的技巧 ⚡

当你只需要对表达式进行分组,而不需要捕获匹配结果时,可以使用非捕获组(Non-capturing Group)。非捕获组不会创建编号的引用(即不参与组计数,仅用于分组匹配,不会存储匹配结果)从而提高性能并避免不必要的内存占用。

基本语法
(?:表达式)
实用示例

区分捕获组与非捕获组

import java.util.regex.Matcher;
import java.util.regex.Pattern;public class GroupExample {public static void main(String[] args) {String text = "apple,banana,orange";// 使用捕获组Pattern capturePattern = Pattern.compile("(apple),(banana)");Matcher captureMatcher = capturePattern.matcher(text);if (captureMatcher.find()) {System.out.println("捕获组数量: " + captureMatcher.groupCount()); // 输出: 2}// 使用非捕获组Pattern nonCapturePattern = Pattern.compile("(?:apple),(?:banana)");Matcher nonCaptureMatcher = nonCapturePattern.matcher(text);if (nonCaptureMatcher.find()) {System.out.println("非捕获组数量: " + nonCaptureMatcher.groupCount()); // 输出: 0}}
}

1.2.3 命名捕获组:提高可读性的高级技巧 🏷️

随着正则表达式变得复杂,仅靠数字引用捕获组会降低代码的可读性。命名捕获组(Named Capturing Group) 允许你为捕获组分配名称,使正则表达式更易于理解和维护。

基本语法
(?<名称>表达式)

引用命名捕获组的语法因语言而异:

  • 在 JavaScript 中:k<名称>$<名称>
  • 在 Python 中:(?P=名称) 或通过 group('名称') 方法
  • 在 Java 中:k<名称>group('名称') 方法
实用示例

使用命名捕获组解析 URL

// 定义正则表达式,用于匹配和解析URL结构。正则表达式使用命名捕获组(如?<protocol>)标识关键部分。
// ^ 表示字符串起始;(?<protocol>https?) 匹配协议(http或https,s为可选)[[2]];
// :// 是固定分隔符;(?<domain>[^/]+) 匹配域名(非斜杠字符序列,直到遇到斜杠或结束);
// (?<path>/.*)? 匹配路径(以斜杠开头的任意字符序列,?表示路径可选)[[5]];$ 表示字符串结束。
const urlRegex = /^(?<protocol>https?)://(?<domain>[^/]+)(?<path>/.*)?$/;// 示例URL字符串,包含协议、域名和路径(含查询参数)
const url = "https://www.example.com/path/to/resource?query=1";// 使用match方法执行正则匹配,返回结果对象。result.groups属性存储命名捕获组的值[[8]]。
const result = url.match(urlRegex);// 输出协议部分:result.groups.protocol访问命名组,匹配"https"(s被捕获)[[2]]
console.log(result.groups.protocol); // 输出: https// 输出域名部分:result.groups.domain访问命名组,匹配"www.example.com"(域名不含斜杠)[[5]]
console.log(result.groups.domain); // 输出: www.example.com// 输出路径部分:result.groups.path访问命名组,匹配"/path/to/resource?query=1"(包含查询参数)[[9]]
console.log(result.groups.path); // 输出: /path/to/resource?query=1

1.3 分组引用:复用匹配结果 🔄

分组引用允许你在正则表达式中或替换操作中复用前面捕获组匹配的内容,极大增强了模式匹配的灵活性和强大性。主要包括以下几种类型:

1.3.1 反向引用:匹配重复文本

反向引用(Backreference) 允许你引用前面捕获组匹配的内容,这对于匹配重复出现的文本模式特别有用。

基本语法:

  • 数字反向引用:\n (n 是捕获组编号)
  • 命名反向引用:\k<名称>(?P=name),后面的内容是 py 专属语法

示例 1:匹配重复的单词

// 定义待检测文本(包含重复单词 "is is" 和 "test test")
const text = "This is is a test test.";// 正则表达式:\b(\w+)\s+\1\b
// - \b:单词边界,确保匹配完整单词
// - (\w+):捕获组1,匹配一个或多个字母/数字/下划线(单词内容)
// - \s+:匹配一个或多个空白字符(如空格)
// - \1:反向引用,指向捕获组1匹配的内容(要求后续内容与组1完全相同)
// - g:全局匹配模式
const duplicateRegex = /\b(\w+)\s+\1\b/g;// 执行匹配:返回所有符合正则的子串数组
const result = text.match(duplicateRegex);
console.log(result); // 输出: [ 'is is', 'test test' ]

示例 2:匹配 HTML 标签对

import re# 定义 HTML 字符串(含成对的 div 和 p 标签)
html = "<div>这是一个 div 标签</div><p>这是一个 p 标签</p>"# 正则表达式:<(?P<tag>\w+)>.*?</(?P=tag)>
# - (?P<tag>\w+):命名捕获组 "tag",匹配标签名(如 "div")
# - .*?:非贪婪匹配标签间任意内容
# - (?P=tag):命名反向引用,要求结束标签名与 "tag" 组相同
pattern = r'<(?P<tag>\w+)>.*?</(?P=tag)>'# 执行匹配:返回所有标签名的列表(仅返回捕获组内容)
matches = re.findall(pattern, html)
print(matches)  # 输出: ['div', 'p']

1.3.2 替换引用:在替换中使用捕获内容

在替换操作中,可以使用特殊语法引用捕获组的内容,实现文本转换和重组。

不同语言中的替换引用语法:

  • JavaScript: $n${n} (数字引用), $<name> (命名引用)
  • Python: n (数字引用), (?P=name) (命名引用)
  • Java: $n (数字引用), ${name} (命名引用)

示例:格式化日期

// 将 yyyy-mm-dd 格式转换为 mm/dd/yyyy 格式
const date = "2023-12-25";
// 使用 $2 和 $3 引用月和日,$1 引用年
const formattedDate = date.replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1");console.log(formattedDate); // 输出: 12/25/2023

1.4 分组的高级应用 🚀

条件表达式:基于捕获组的条件匹配 🧩

条件表达式(Conditional Expression) 允许根据前面的捕获组是否匹配来决定应用哪个模式。

基本语法:

(?(组号或名称)匹配成功时的模式|匹配失败时的模式)

示例:处理可选的引号

import retext = 'name=John name="Doe"'# 正则表达式分解:
# 1. name= : 匹配字面量
# 2. (?:...) : 非捕获组,表示两种匹配可能
# 3. "(?P<quoted>[^"]+)" : 带引号的情况
#    - " : 匹配开头的引号
#    - (?P<quoted>[^"]+) : 命名捕获组,匹配除"外的任意字符(至少1个)
#    - " : 匹配结尾的引号
# 4. | : 或逻辑
# 5. (?P<unquoted>\w+) : 不带引号的情况
#    - \w+ : 匹配字母/数字/下划线(至少1个)
pattern = r'name=(?:"(?P<quoted>[^"]+)"|(?P<unquoted>\w+))'# 使用finditer获取所有匹配的迭代器对象(保持匹配顺序)
matches = re.finditer(pattern, text)for match in matches:# 优先检查带引号的分组(因为正则中带引号的模式在前)if match.group('quoted'):# 当quoted分组有匹配内容时,输出带引号的值print(f'带引号的值: {match.group("quoted")}')  # 注意使用双引号避免冲突else:# 当unquoted分组有匹配内容时,输出普通值print(f'不带引号的值: {match.group("unquoted")}')# 输出逻辑说明:
# 第一个匹配 name=John → 触发unquoted分组
# 第二个匹配 name="Doe" → 触发quoted分组(Doe被[^"]+捕获)

总结:掌握分组技术,提升正则表达式能力 🚀

通过本章学习,我们深入探讨了正则表达式的分组与引用技术,包括:

  • 基础分组:使用 ()创建捕获组,提取匹配的子串
  • 性能优化:使用 (?:)非捕获组减少内存占用
  • 可读性提升:使用命名捕获组 (?<name>)增强代码可维护性
  • 高级应用:掌握反向引用、条件表达式等高级技巧

正则表达式的分组技术是处理复杂文本模式的核心工具。熟练掌握这些技巧,能够让你轻松解决各种文本处理难题,从简单的数据提取到复杂的嵌套结构分析。

正则表达式是程序员的瑞士军刀,而分组技术则是这把军刀中最锋利的刀刃之一!🔪

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/89265.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/89265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python+requests 接口自动化测试实战

首先介绍一下python的requests模块&#xff1a; requests的使用介绍&#xff1a;requests快速入门 Python结合requests库实现接口自动化测试环境说明&#xff1a; 1.WIN 7, 64位 2.Python3.4.3 &#xff08;pip-8.1.2&#xff09; 3.Requests —>pip install requests 4.U…

NAT 实验

NAT 实验 一.实验拓扑图实验目的 1.按照图示配置 IP 地址 2.私网 A 通过 R1 接入到互联网&#xff0c;私网 B 通过 R3 接入到互联网 3.私网 A 内部存在 Vlan10 和 Vlan20&#xff0c;通过 R1 上单臂路由访问外部网络 4.私网 A 通过 NAPT 使 Vlan10 和 Vlan20 都能够使用 R1 的公…

buuctf——web刷题第三页

第三页 目录 [FBCTF2019]RCEService [0CTF 2016]piapiapia [Zer0pts2020]Can you guess it? [WUSTCTF2020]颜值成绩查询 [SUCTF 2019]Pythonginx [MRCTF2020]套娃 [CSCCTF 2019 Qual]FlaskLight [watevrCTF-2019]Cookie Store [WUSTCTF2020]CV Maker [红明谷CTF 202…

前后端分离项目中的接口设计与调用流程——以高仙机器人集成为例

一、背景介绍在前后端分离项目开发中&#xff0c;前端页面需要频繁调用后端接口获取数据。在高仙机器人对接项目中&#xff0c;我们采用了若依&#xff08;RuoYi&#xff09;框架&#xff0c;前端通过统一的 API 封装与后端进行数据交互&#xff0c;而后端再对接高仙官方的 OPE…

【第五节】部署http接口到ubuntu server上的docker内

描述清楚需求&#xff0c;让deepseek帮我们写一个demo&#xff0c;文件结构如下 FLASK_API_001 ├── app.py └── Dockerfile └── requirements.txtapp.pyfrom flask import Flask, jsonify, requestapp Flask(__name__)# 根路由 app.route(/) def home():return "…

在 IntelliJ IDEA 中添加框架支持的解决方案(没有出现Add Framework Support)

在 IntelliJ IDEA 中添加框架支持的解决方案 问题背景 版本变化&#xff1a;在 IntelliJ IDEA 2023.2 及更高版本中&#xff0c;项目右键菜单中的 “Add Framework Support” 选项已被移除。 常见需求&#xff1a;为 Java 项目添加框架支持&#xff08;如 Maven、Spring 等&am…

北京-4年功能测试2年空窗-报培训班学测开-第五十天

咦&#xff0c;昨天路上写一半就到家了&#xff0c;后来想早点睡就忘了还要发了&#xff0c;现在赶紧补上昨天是最后一节课(我们将一整天的课称为一节&#xff09;&#xff0c;这就结课了昨天讲了简历编写&#xff0c;面试要准备的内容&#xff0c;还有redis和docker也没有什么…

华为鸿蒙HarmonyOpenEye项目:开眼App的鸿蒙实现之旅

华为鸿蒙HarmonyOpenEye项目&#xff1a;开眼App的鸿蒙实现之旅 引言 在当今移动应用开发的浪潮中&#xff0c;鸿蒙系统凭借其独特的分布式能力和高效的开发框架&#xff0c;吸引了众多开发者的目光。今天要给大家介绍的是一个基于华为鸿蒙系统开发的开眼App项目——HarmonyO…

代码随想录day36dp4

文章目录1049.最后一块石头的重量II494.目标和474.一和零1049.最后一块石头的重量II 题目链接 文章讲解 class Solution { public:int lastStoneWeightII(vector<int>& stones) {// 1. 确定 DP 数组及下标的含义&#xff1a;// dp[i][j] 表示考虑前 i 块石头&#…

Python 爬虫实战指南:按关键字搜索商品

在电商领域&#xff0c;按关键字搜索商品并获取其详情信息是一项常见的需求。无论是进行市场调研、竞品分析还是用户体验优化&#xff0c;能够快速准确地获取商品信息都至关重要。1688 作为国内领先的 B2B 电商平台&#xff0c;提供了丰富的商品资源。本文将详细介绍如何使用 P…

【源力觉醒 创作者计划】百度AI的开放新篇章:文心4.5本地化部署指南与未来生态战略展望

百度AI的开放新篇章&#xff1a;文心4.5本地化部署指南与未来生态战略展望 一起来玩转文心大模型吧&#x1f449;文心大模型免费下载地址&#xff1a;https://ai.gitcode.com/theme/1939325484087291906 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30…

测试工作中的质量门禁管理

一、前言 测试阶段的质量门禁设计要考虑几个维度,首先是研发流程的阶段划分,每个阶段都要有明确的准入准出标准;其次要考虑不同测试类型的特点,比如功能测试和性能测试的验收标准肯定不同;最后还要平衡质量要求和项目进度。 在单元测试阶段,可以设置通过率和覆盖率的阈值…

线上分享:解码eVTOL安全基因,构建安全飞行生态

随着城市空中交通&#xff08;UAM&#xff09;快速发展&#xff0c;电动垂直起降飞行器&#xff08;eVTOL&#xff09;面临严格的安全与可靠性要求&#xff0c;需满足全球适航标准及全生命周期分析。安全与可靠的飞行系统成为行业关注的焦点。在此背景下&#xff0c;本期线上分…

C回调函数基础用法

&#x1f4cc; 定义&#xff1a;回调函数是通过函数指针传递给另一个函数的函数&#xff0c;这个被传进去的函数将在某个时刻被“回调”调用。换句话说&#xff1a;你定义一个函数 A把函数 A 的地址&#xff08;即函数指针&#xff09;作为参数传给函数 B函数 B 在合适的时机调…

手撕设计模式之消息推送系统——桥接模式

手撕设计模式之消息推送系统——桥接模式 1.业务需求 ​ 大家好&#xff0c;我是菠菜啊&#xff0c;好久不见&#xff0c;今天给大家带来的是——桥接模式。老规矩&#xff0c;在介绍这期内容前&#xff0c;我们先来看看这样的需求&#xff1a;我们现在要做一个消息推送系统&…

Java 大厂面试题 -- JVM 垃圾回收机制大揭秘:从原理到实战的全维度优化

最近佳作推荐&#xff1a; Java 大厂面试题 – JVM 面试题全解析&#xff1a;横扫大厂面试&#xff08;New&#xff09; Java 大厂面试题 – 从菜鸟到大神&#xff1a;JVM 实战技巧让你收获满满&#xff08;New&#xff09; Java 大厂面试题 – JVM 与云原生的完美融合&#xf…

图机器学习(9)——图正则化算法

图机器学习&#xff08;9&#xff09;——图正则化算法1. 图正则化方法2. 流形正则化与半监督嵌入3. 神经图学习4. Planetoid1. 图正则化方法 浅层嵌入方法已经证明&#xff0c;通过编码数据点间的拓扑关系可以构建更鲁棒的分类器来处理半监督任务。本质上&#xff0c;网络信息…

视频动态范围技术演进:从SDR到HDR的影像革命

一、动态范围技术基础认知 1.1 人眼视觉特性与动态范围 人眼的动态感知范围可达106:1&#xff08;0.0001-105 cd/m&#xff09;&#xff0c;远超传统显示设备能力。视网膜通过虹膜调节&#xff08;物理孔径&#xff09;与光化学反应&#xff08;光敏蛋白分解&#xff09;实现16…

基于LAMP环境的校园论坛项目

1.配置本地仓库a.修改主机名为自己姓名全拼[rootserver ~]# hostnamectl set-hostname jun [rootserver ~]# bash [rootjun ~]# 运行结果图如下图所示&#xff1a;b.光盘挂载到/mnt目录下[rootjun yum.repos.d]# mount /dev/sr0 /mnt mount: /mnt: WARNING: source write-prote…

在物联网系统中时序数据库和关系型数据库如何使用?

在物联网系统中&#xff0c;时序数据库&#xff08;TSDB&#xff09;和关系型数据库&#xff08;RDBMS&#xff09;的存储顺序设计需要根据数据特性、业务需求和系统架构综合考虑。以下是典型的设计方案和逻辑顺序&#xff1a;1. 常见存储顺序方案 方案一&#xff1a;先写时序数…