正则表达式是国际标准,跨越语言;
正则表达式是一个规则,用于验证字符串;
使用场景
数据验证
:可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式;
替换文本
:可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它;
从字符串中提取子字符串
:可以查找文档内或输入域内特定的文本;
介绍
量词元字符
字符 | 描述 |
---|---|
* | 0 到多次,* 是贪婪的,会尽可能多的匹配文字,在它后面加上一个 ? 就可以实现非贪婪或最小匹配 |
+ | 1 到多次,+ 是贪婪的,会尽可能多的匹配文字,在它后面加上一个 ? 就可以实现非贪婪或最小匹配 |
? | 0 或 1 次 |
{ n } | 出现 n 次 (n 为 0 或正整数) |
{ n, } | 出现 n 到多次 (n 为 0 或正整数) |
{ n, m } | 出现 n-m 次 (n 为 0 或正整数) |
字符类
字符 | 描述 |
---|---|
[ ] | 匹配括号内的任意一个字符 /[abc]/g 匹配所有的字符 “a”、“b”、“c”, /[0-9]/g 匹配所有 0-9 中的数字 |
[^ ] | 匹配除了括号内的字符以外的任意一个字符 /[^abc]/g 匹配除了字符 “a”、“b”、“c” 以外的所有任意字符 |
匹配特定类型的字符
字符 | 描述 |
---|---|
\d | 匹配一个数字字符,等价于 /[0-9]/ |
\D | 匹配一个非数字字符,等价于 /[^0-9]/ |
\w | 匹配字母、数字、下划线,等价于 /[A-Za-z0-9_]/ |
\W | 匹配非字母、数字、下划线,等价于 /[^A-Za-z0-9_]/ |
特殊字符
字符 | 描述 |
---|---|
\ | 转义字符,用于匹配特殊字符本身 |
. | 匹配除换行符 (\n、\r) 之外的任何单个字符,相等于 /[^\n\r]/ |
| | 用于指定多个模式的选择 |
非打印字符
字符 | 描述 |
---|---|
\n | 匹配一个换行符 |
\r | 匹配一个回车符 |
\s | 匹配任何空白字符,包括空格、制表符、换页符等等 |
\S | 匹配任何非空白字符 |
\t | 匹配一个制表符 |
修饰符
字符 | 描述 |
---|---|
i | 不区分大小写,将匹配设置为不区分大小写,搜索时不区分大小写 |
m | 多行匹配,使边界字符 ^ 和 $ 匹配每一行的开头 (\n 之后的位置) 和结尾 (\n 之前的位置),而不是整个字符串的开头和结尾 |
g | 全局匹配,查找所有的匹配项 |
边界匹配
字符 | 描述 |
---|---|
^ | 匹配字符串的开头 (精确匹配) |
$ | 匹配字符串的结尾 (精确匹配) |
\b | 匹配单词边界,它匹配一个 单词的开始 或 单词的结束 的位置,而不匹配任何实际的字符 \bword\b 匹配整个单词 “word”,但不匹配 “words” 或 “sword” |
\B | 匹配非单词边界,即匹配 单词的内部、非单词的开头或结尾 \Bword\B 匹配 “swords” 中的 “word”,但不匹配 “word”、“sword”、 “words” |
分组
-
分组使用圆括号 () 来创建,主要作用是:
- 将多个字符视为一个整体进行操作;
- 实现子表达式的逻辑组合;
- 为捕获和反向引用奠定基础;
-
基本用法示例:
console.log(/(a|b)c/.exec('abc')); // 匹配"ac"或"bc" // 输出 ['bc', 'b', index: 1, input: 'abc', groups: undefined]
-
非捕获分组:有时只需要分组功能而不需要捕获内容,可以使用 (?:) 语法
console.log(/(?:a|b)c/.exec('abc')); // 同样匹配"ac"或"bc",但不会捕获分组内容 // 输出 ['bc', index: 1, input: 'abc', groups: undefined]
捕获
-
当使用普通圆括号 () 时,正则引擎会自动 “捕获” 该分组匹配的内容,可以通过编号或名称来引用这些捕获的内容;
-
编号捕获:捕获的分组按左括号出现的顺序编号,从 1 开始 (0 表示整个匹配);
console.log(/(\d{4})-(\d{2})-(\d{2})/.exec('2025-12-23')); // 匹配日期格式,有3个捕获组 // [ // '2025-12-23', // '2025', 组 1:年(\d {4} 匹配的内容) // '12', 组 2:月(\d {2} 匹配的内容) // '23', 组 3:日(\d {2} 匹配的内容) // index: 0, // input: '2025-12-23', // groups: undefined // ]
-
命名捕获:可以为分组指定名称,使引用更清晰;
console.log(/(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/g.exec('2025-12-23')); // [ // '2025-12-23', // '2025', 组 1:年(\d {4} 匹配的内容) // '12', 组 2:月(\d {2} 匹配的内容) // '23', 组 3:日(\d {2} 匹配的内容) // index: 0, // input: '2025-12-23', // groups: {year: '2025', month: '12', day: '23'} // ]
-
反向引用:在正则表达式内部,可以用 \n (其中 n 是一个数字,代表第 n 个捕获组) 引用之前捕获的内容,常用于匹配重复模式;
JavaScriptJavaScriptJavaScriptJavaScript// 1. 匹配重复单词 console.log(/(\w+)\s+\1/.exec('hello hello')); // ['hello hello', 'hello', index: 0, input: 'hello hello', groups: undefined]
// 2. 匹配 HTML 标签对 console.log(/<(\w+)>.*?<\/\1>/.exec('<p>content</p>')); // <(\w+)> 匹配开始标签并捕获标签名 // .*? 匹配标签内容(非贪婪模式) // </\1> 匹配对应的结束标签,\1引用前面捕获的标签名 // ['<p>content</p>', 'p', index: 0, input: '<p>content</p>', groups: undefined]
// 3. 匹配对称的引号 console.log(/(["'])(.*?)\1/.exec(`"hello world"`)); // ['"hello world"', '"', 'hello world', index: 0, input: '"hello world"', groups: undefined]
// 4. 找出该字符串中连续的字符 var s = "aaaabbbbdefgg"; var reg = /(\w)\1+/g; while (result = reg.exec(s)) { console.log(result); } // [ 'aaaa', 'a', index: 0, input: 'aaaabbbbdefgg', groups: undefined ] // [ 'bbbb', 'b', index: 4, input: 'aaaabbbbdefgg', groups: undefined ] // [ 'gg', 'g', index: 11, input: 'aaaabbbbdefgg', groups: undefined ]
正向预查和负向预查
-
预查是一种 零宽度 断言,它不会消耗字符串中的字符,仅用于判断当前位置的前后是否满足特定条件;预查的核心价值是 “筛选位置” 而非 “匹配内容”,这使得它在复杂文本提取、格式验证等场景中极为灵活;
-
“零宽度” 的含义:正则匹配时,会有一个 “匹配指针” 在字符串上移动,常规元字符 (如\d、a-z) 匹配内容后,指针会向后移动对应长度 (例如匹配 “123” 中的 \d,指针会从位置 0 移到 1) ;而预查仅判断 “当前指针位置是否符合条件”,判断后指针不移动 (宽度为 0);
-
四大类型预查对比:
预查类型 语法格式 核心逻辑 (当前位置) 应用场景举例 肯定正向预查 A(?=B) 后面是 B,匹配 A 提取带特定后缀的内容 (如.png前的文件名) 否定正向预查 A(?!B) 后面不是 B,匹配 A 排除特定后缀的内容 (如.tmp文件) 肯定负向预查 (?<=A)B 前面是 A,匹配 B 提取带特定前缀的内容 (如ID: 后的数字) 否定负向预查 (?<!A)B 前面不是 A,匹配 B 排除带特定前缀的内容 (如http://开头的 URL)
肯定正向预查
-
判断当前位置后面的字符必须符合指定规则,符合则当前位置有效,否则无效;
-
语法:要匹配的内容(?=预查条件) “匹配 A,且 A 的后面是 B”(A 是要匹配的内容,B 是预查条件);
-
案例:
JavaScriptJavaScript// 1. 提取带特定后缀的内容(从文件名列表中提取 “以 .png、.csv 结尾的文件名,不含后缀) console.log("logo.png, data.csv, bg.png, report.pdf".match(/\w+(?=\.(png|csv))/g)); // ['logo', 'data', 'bg']
// 2. 验证密码是否包含数字 console.log(/^(?=.*\d).{6,12}$/.test('123456')); // true // 解析: // ^:字符串开头 // (?=.*\d):判断 “从开头到结尾的任意位置是否有数字”(.*表示任意字符任意次,\d表示数字) // .{6,12}:匹配 6-12 个任意字符(密码主体) // $:字符串结尾
否定正向预查
-
判断当前位置后面的字符必须不符合指定规则,不符合则当前位置有效,否则无效;
-
语法:要匹配的内容(?!预查条件) “匹配 A,且 A 的后面不是 B”(A 是要匹配的内容,B 是预查条件);
-
案例:
JavaScriptJavaScript// 1. 排除特定后缀的文件(从文件列表中排除 “.tmp临时文件”,提取其他文件) console.log("note.txt, temp.tmp, data.xlsx, log.tmp".match(/\w+\.(?!tmp\b)\w+/g)); // ['note.txt', 'data.xlsx']
// 2. 禁止敏感词出现在文本开头(判断用户名是否 “不以 admin 开头”) console.log(/^(?!admin).+$/.test('admin123')); // false
肯定负向预查
-
判断当前位置前面的字符必须符合指定规则,符合则当前位置有效,否则无效;
-
语法:(?<=预查条件)要匹配的内容 “匹配 B,且 B 的前面是 A”;
-
案例:
JavaScriptJavaScript// 1. 提取 “前缀固定” 的内容(从日志中提取 “ID: 后面的用户 ID”) console.log("Login: user1, ID: 1001; Login: user2, ID: 1002".match(/(?<=ID: )\d+/g)); // ['1001', '1002'] console.log("Login: user1, ID: 1001; Login: user2, ID: 1002".match(/\w+: \b(?<=ID: )\d+/g)); // ['ID: 1001', 'ID: 1002']
// 2. 格式化数字(给整数部分加千分位) console.log("987654321.123".replace(/(?<=\d)(?=(\d{3})+\.)/g, ',')); // '987,654,321.123' // 解析: // (?<=\d):肯定负向预查,确保当前位置前面是数字(避免在开头加逗号) // (?=(\d{3})+\.):肯定正向预查,确保当前位置后面是 “若干个 3 位数字 + 小数点”(即从当前位置到小数点,数字位数是 3 的倍数)
否定负向预查
-
判断当前位置前面的字符必须不符合指定规则,不符合则当前位置有效,否则无效;
-
语法:(?<!预查条件)要匹配的内容 “匹配 B,且 B 的前面不是 A”;
-
案例:
JavaScriptJavaScript// 1. 排除 “前缀敏感” 的内容(从文本中提取 “不是http://开头” 的 URL) console.log("http://baidu.com, https://google.com, ftp://ftp.server.com".match(/(?<!http:)\/\/.+/g)); // ['//google.com, ftp://ftp.server.com']
// 2. 避免重复替换(给文本中的 “book” 加引号,但 “已经有引号的"book"” 不重复加) console.log(`I have a book, and a "book".`.replace(/(?<!")book(?!")/g, '"book"')); // 'I have a "book", and a "book".'
在线工具
169.多数元素
上一篇