当前位置: 首页 > news >正文

JavaScript 正则表达式:选择、分组与引用深度解析

在正则表达式中,“选择、分组、引用” 是实现复杂匹配逻辑的核心语法,能够将多个简单规则组合成灵活的匹配模板。

一、选择(|):实现 “或” 逻辑匹配

选择符(|)用于匹配 “多个模式中的任意一个”,相当于逻辑中的 “或”(OR),是正则中实现多规则匹配的基础。

1. 核心定义

选择符 | 分隔两个或多个候选模式,正则会依次尝试匹配每个模式,只要有一个模式匹配成功,整个正则就返回匹配结果。

2. 语法规则

  • 基础语法:pattern1|pattern2|pattern3(匹配 pattern1、pattern2、pattern3 中的任意一个);
  • 优先级:| 的优先级最低,若需限定选择范围,需配合分组 () 使用(否则会匹配 “左侧全量 + 右侧全量”);
  • 匹配顺序:正则会从左到右尝试候选模式,一旦匹配成功就停止后续尝试(即 “左优先匹配”)。

3. 代码案例与运行结果

案例 1:基础选择匹配(匹配多个固定字符串)

javascript

运行

// 匹配 "apple"、"banana"、"orange" 中的任意一个
const reg = /apple|banana|orange/;
const fruits = ["apple", "banana", "orange", "grape"];fruits.forEach(fruit => {console.log(`${fruit}: ${reg.test(fruit)}`);
});
// 运行结果:
// apple: true(匹配 "apple")
// banana: true(匹配 "banana")
// orange: true(匹配 "orange")
// grape: false(无匹配)
案例 2:选择符与分组配合(限定选择范围)

若不使用分组,选择符会匹配 “左侧全量 + 右侧全量”,导致逻辑错误,需用 () 限定选择范围:

javascript

运行

const str1 = "I like apples";
const str2 = "I like oranges";
const str3 = "I like grape";// 错误写法:未分组,匹配 "I like app" 或 "les"(逻辑不符合预期)
const wrongReg = /I like app|les/;
console.log(wrongReg.test(str1)); // true(错误匹配 "I like app")
console.log(wrongReg.test(str2)); // false(无匹配)// 正确写法:用 () 限定选择范围,匹配 "I like apple" 或 "I like orange"
const rightReg = /I like (apple|orange)/;
console.log(rightReg.test(str1)); // true(匹配 "I like apple")
console.log(rightReg.test(str2)); // true(匹配 "I like orange")
console.log(rightReg.test(str3)); // false(无匹配)
案例 3:选择符的左优先匹配特性

正则会从左到右尝试候选模式,一旦匹配成功就停止,即使右侧有更优匹配:

javascript

运行

// 匹配 "abc" 或 "abcd"(左优先:先尝试 "abc")
const reg = /abc|abcd/;
const str = "abcd";// 匹配结果:优先匹配左侧的 "abc",而非完整的 "abcd"
console.log(str.match(reg)); // ["abc", index: 0, input: "abcd", groups: undefined]

4. 实战场景:匹配多种日期格式

用选择符匹配 yyyy-mm-ddyyyy/mm/ddyyyy.mm.dd 三种日期格式:

javascript

运行

// 匹配三种日期格式(年-月-日、年/月/日、年.月.日)
const dateReg = /^\d{4}(-|\/|\.)\d{2}\1\d{2}$/; 
// 注:\1 是分组引用,后续会讲解,此处用于保证分隔符一致(如 "-" 前后一致)console.log(dateReg.test("2024-05-20")); // true(匹配 "-" 格式)
console.log(dateReg.test("2024/05/20")); // true(匹配 "/" 格式)
console.log(dateReg.test("2024.05.20")); // true(匹配 "." 格式)
console.log(dateReg.test("2024-05/20")); // false(分隔符不一致)

二、分组(()):将多个字符视为一个整体

分组符(())用于将多个字符或子模式 “打包” 成一个整体,实现 “批量控制”(如批量限定量词、批量参与选择),是正则中实现复杂规则的核心。

1. 核心定义

分组符 () 会将括号内的内容视为一个 “子模式”,可以对这个子模式整体应用量词、选择符等规则,同时分组会捕获匹配结果(用于后续引用)。

2. 分组的分类与语法

根据是否捕获匹配结果,分组可分为 “捕获组” 和 “非捕获组”,常用分类如下:

分组类型语法描述
普通捕获组(pattern)捕获括号内 pattern 的匹配结果,可通过 \n(正则内)或 result[n](代码内)引用
非捕获组(?:pattern)仅将 pattern 视为整体,不捕获匹配结果(节省性能,无需引用时推荐)
正向前瞻分组(?=pattern)不捕获结果,仅判断当前位置后是否满足 pattern(前面文章已讲,此处不展开)
负向前瞻分组(?!pattern)不捕获结果,仅判断当前位置后是否不满足 pattern

3. 代码案例与运行结果

案例 1:普通捕获组(批量限定量词)

将 ab 视为一个整体,匹配其重复 2 次的场景:

javascript

运行

// 分组前:匹配 "a" 重复 2 次 + "b"(即 "aab")
const noGroupReg = /a{2}b/;
// 分组后:匹配 "ab" 重复 2 次(即 "abab")
const groupReg = /(ab){2}/;console.log(noGroupReg.test("aab")); // true(分组前,匹配 "aab")
console.log(noGroupReg.test("abab")); // false(分组前,不匹配)
console.log(groupReg.test("abab")); // true(分组后,匹配 "abab")
console.log(groupReg.test("aab")); // false(分组后,不匹配)
案例 2:非捕获组(仅整体控制,不捕获结果)

当不需要引用分组结果时,用 (?:pattern) 定义非捕获组,避免不必要的结果存储,提升性能:

javascript

运行

// 非捕获组:匹配 "apple" 或 "banana",不捕获结果
const nonCaptureReg = /(?:apple|banana)/;
const str = "I like apple";// 匹配结果:仅返回整体匹配内容,无分组捕获结果
const result = str.match(nonCaptureReg);
console.log(result); // ["apple", index: 7, input: "I like apple", groups: undefined]
console.log(result[1]); // undefined(非捕获组,无分组结果)// 普通捕获组:捕获 "apple" 或 "banana" 的结果
const captureReg = /(apple|banana)/;
const captureResult = str.match(captureReg);
console.log(captureResult[1]); // "apple"(普通捕获组,可获取分组结果)
案例 3:分组嵌套(复杂子模式控制)

分组支持嵌套,嵌套分组的捕获顺序为 “左括号出现的顺序”(即第 1 个左括号为组 1,第 2 个为组 2,以此类推):

javascript

运行

// 嵌套分组:匹配 "a(bc(d))"(组 1:"bc(d)",组 2:"d")
const reg = /a(bc(d))/;
const str = "abcde";
const result = reg.exec(str);console.log(result[0]); // "abcd"(整体匹配结果)
console.log(result[1]); // "bcd"(第 1 个左括号对应的分组结果)
console.log(result[2]); // "d"(第 2 个左括号对应的分组结果)

4. 实战场景:提取 URL 中的域名和路径

用分组提取 URL(如 https://www.baidu.com/s?wd=regex)中的 “域名”(www.baidu.com)和 “路径”(/s):

javascript

运行

function extractUrlParts(url) {// 分组1:域名(如 www.baidu.com),分组2:路径(如 /s)const reg = /https?:\/\/([^\/]+)(\/[^?]*)/;const result = url.match(reg);if (result) {return {domain: result[1], // 第 1 组:域名path: result[2]    // 第 2 组:路径};}return null;
}const url = "https://www.baidu.com/s?wd=regex";
const parts = extractUrlParts(url);
console.log(parts);
// 运行结果:
// { domain: "www.baidu.com", path: "/s" }

三、引用(\n 与 $n):复用分组捕获结果

引用是基于分组的延伸特性,用于 “复用之前分组的捕获结果”,分为 “正则内引用”(\n)和 “代码内引用”($n 或 result[n]),能够实现 “重复匹配”“动态替换” 等高级功能。

1. 核心定义

  • 正则内引用:用 \n(n 为分组序号,从 1 开始)在正则表达式内部引用第 n 个分组的捕获结果;
  • 代码内引用:在代码中(如 replace 方法)用 $n 引用第 n 个分组的结果,或通过 exec/match 的返回数组 result[n] 获取。

2. 语法规则

  • 分组序号:从左到右数左括号的顺序,第 1 个左括号为组 1,第 2 个为组 2,以此类推;
  • 非捕获组:(?:pattern) 不捕获结果,无法被引用;
  • 引用范围:\n 仅在正则内部生效,$n 仅在 replace 等字符串方法中生效,result[n] 适用于所有代码场景。

3. 代码案例与运行结果

案例 1:正则内引用(\n):匹配重复内容

用 \1 引用第 1 组的结果,匹配 “前后重复的单词”(如 hello hello):

javascript

运行

// 分组1:匹配单词(\w+),\1:引用分组1的结果,匹配重复单词
const reg = /(\w+)\s+\1/;
const str1 = "hello hello";
const str2 = "hello world";
const str3 = "apple apple banana";console.log(reg.test(str1)); // true(匹配 "hello hello")
console.log(reg.test(str2)); // false(无重复单词)
console.log(str3.match(reg)); // ["apple apple", "apple"](匹配 "apple apple",分组1结果为 "apple")
案例 2:代码内引用($n):动态替换内容

在 replace 方法中用 $1$2 引用分组结果,实现 “格式化手机号”(中间 4 位打码):

javascript

运行

// 分组1:前 3 位数字,分组2:后 4 位数字
const phoneReg = /(\d{3})\d{4}(\d{4})/;
const phone = "13812345678";// $1 引用分组1(138),$2 引用分组2(5678),中间用 **** 替换
const formattedPhone = phone.replace(phoneReg, "$1****$2");
console.log(formattedPhone); // 运行结果:138****5678
案例 3:代码内引用(result [n]):提取分组详情

通过 exec 或 match 的返回数组获取分组结果,提取 “身份证号中的出生日期”(第 7-14 位):

javascript

运行

// 身份证号规则:18位,第 7-14 位为出生日期(yyyyMMdd)
const idCardReg = /^(\d{6})(\d{8})(\d{4})$/;
const idCard = "110101200001011234";const result = idCardReg.exec(idCard);
if (result) {console.log("地区码:", result[1]); // 地区码:110101(第 1 组)console.log("出生日期:", result[2]); // 出生日期:20000101(第 2 组)console.log("校验码:", result[3]); // 校验码:1234(第 3 组)
}
// 运行结果:
// 地区码: 110101
// 出生日期: 20000101
// 校验码: 1234
案例 4:引用的边界场景(越界引用)

若引用的分组序号不存在(如引用第 3 组,但仅定义了 2 个分组),\n 会被视为普通字符:

javascript

运行

// 仅定义 2 个分组,引用第 3 组(越界)
const reg = /(\d{2})-(\d{2})\3/;
const str1 = "05-203"; // 末尾为 "3",与 \3(视为普通字符 "3")匹配
const str2 = "05-204"; // 末尾为 "4",不匹配console.log(reg.test(str1)); // true(\3 视为 "3",匹配 "05-203")
console.log(reg.test(str2)); // false(不匹配)

4. 实战场景:修复 HTML 标签(闭合不匹配的标签)

用引用修复 “标签名不匹配” 的 HTML(如 <div>content</p>),将闭合标签改为与开始标签一致:

javascript

运行

function fixHtmlTag(html) {// 分组1:匹配开始标签名(如 div),替换闭合标签为 $1(与开始标签一致)const reg = /<(\w+)>([\s\S]*?)<\/\w+>/;return html.replace(reg, "<$1>$2</$1>");
}const wrongHtml = "<div>这是错误的标签</p>";
const fixedHtml = fixHtmlTag(wrongHtml);
console.log(fixedHtml); // 运行结果:<div>这是错误的标签</div>

四、选择、分组、引用的组合实战

场景:验证并提取 “带区号的固定电话”

固定电话规则:

  1. 区号:3-4 位数字,可选(如 010- 或 021-);
  2. 号码:7-8 位数字;
  3. 分隔符:区号与号码之间用 - 或 (空格)分隔;
  4. 整体格式:[区号][分隔符][号码](区号可选)。
实现代码

javascript

运行

function validateAndExtractLandline(phone) {// 分组1:区号(可选,3-4位数字),分组2:分隔符(- 或 空格),分组3:号码(7-8位数字)const reg = /^(?:(\d{3,4})([- ]))?(\d{7,8})$/;const result = phone.match(reg);if (!result) {return "固定电话格式错误";}return {fullPhone: result[0],areaCode: result[1] || "无区号", // 区号可选,无则返回 "无区号"number: result[3]};
}// 测试案例
const phones = ["010-12345678",  // 带区号(4位)+ "-" 分隔"021 87654321",  // 带区号(3位)+ 空格分隔"1234567",       // 无区号(7位号码)"010-1234567"    // 错误(号码仅 7 位,但区号+分隔符存在,需 8 位号码)
];phones.forEach(phone => {console.log(phone + ":", validateAndExtractLandline(phone));
});
运行结果

plaintext

010-12345678: { fullPhone: '010-12345678', areaCode: '010', number: '12345678' }
021 87654321: { fullPhone: '021 87654321', areaCode: '021', number: '87654321' }
1234567: { fullPhone: '1234567', areaCode: '无区号', number: '1234567' }
010-1234567: 固定电话格式错误

五、避坑指南

  1. 选择符的优先级问题| 优先级最低,若需限定选择范围,必须配合分组 (),否则会匹配 “左侧全量 + 右侧全量”(如 /a|bc/ 匹配 a 或 bc,而非 ab 或 ac);
  2. 分组的捕获与性能:无需引用分组结果时,优先用非捕获组 (?:pattern),避免存储无用的捕获结果,提升正则匹配效率;
  3. 引用的序号规则:分组序号按 “左括号出现顺序” 计数,嵌套分组需仔细数清序号(如 (a(b(c))) 中,组 1:a(b(c)),组 2:b(c),组 3:c);
  4. 全局匹配的引用问题:用 g 修饰符时,exec 会循环匹配,每次匹配的分组结果需通过 result[n] 实时获取,不可复用之前的分组结果。

总结

选择(|)、分组(())、引用(\n/$n)是正则表达式的 “三驾马车”:

  • 选择:实现多规则 “或” 逻辑,解决 “匹配多个候选模式” 的需求;
  • 分组:将多个字符打包成整体,支持批量控制和结果捕获,是复杂规则的基础;
  • 引用:复用分组结果,实现重复匹配和动态替换,提升正则的灵活性。
http://www.dtcms.com/a/578351.html

相关文章:

  • 20251106给荣品RD-RK3588-MID开发板跑Rockchip的原厂Android14系统时更换wallpaper墙纸
  • 桂林做网站多少钱做网站给不给源代码
  • 如何使用 Nodemon 自动重启 Node.js 应用
  • Tongweb8安装部署
  • 汽车网站设计模板网页图片批量下载
  • Playwright高级用法全解析:从自动化到工程化的进阶指南
  • 【开源】FBro 浏览器工作流自动化系统
  • 从手动到智能:XTOM-STATION自动化检测中心在复杂曲面零件全尺寸检测中的应用
  • 【题解】洛谷 P4201 [NOI2008] 设计路线 [树形 DP]
  • ES索引配置字段解读
  • linux服务器自动备份数据库脚本
  • 免费的微信视频号下载器
  • 做ic销售的各种网站dede网站安全设置
  • 绥化建设网站电子商城系统平台
  • 影刀RPA一键生成直播链接!AI智能选品,效率提升2000%[特殊字符]
  • 个人网站代码编写海口创建公司
  • 用jsp实现网站开发的流程没有网站如何做SEO推广有用吗
  • 什么网站专门做境外当地游手机网站设计标准
  • 一些题目~~~
  • 5. Unix/Linux 系统常用类型别名清单
  • Transformers之外的注意力机制
  • js(DOM)基础:12、DOM查询3、DOM增删改、DOM删除列表、DOM添加列表、DOM操作遇到问题
  • 论项目的风险管理及应用
  • 上海闸北区网站建设初期做网站
  • 小型公司网站建设知乎网站建设与开发开题报告
  • 高端网站的制作有那些网站
  • UE4第二次构建时,引擎是否主动删除掉Saved/Cooked目录
  • asynccontextmanager
  • 天津大学邓意达/陈亚楠团队Nano-Micro Lett.研究:热冲击法促新型纳米片自发成长,提升全水解效率
  • 流程架构的解耦与进化设计