元字符与量词
上一篇介绍了字符类和锚点,本文继续深入正则的"语法骨架":元字符、量词、分组与反向引用、或操作、转义。这些是写出复杂模式所必须的工具,也是面试中"手写正则"题的核心考察点。
元字符
元字符是正则中有特殊含义的字符,不再是字面量本身。一共需要记住以下几个:
| 元字符 | 含义 |
|---|---|
\ | 转义 |
^ | 行首锚点 / 字符类取反 |
$ | 行尾锚点 |
. | 除换行符外任意字符 |
| ` | ` |
? * + | 量词 |
() | 分组 |
[] | 字符类 |
{} | 量词(重复次数) |
要在正则中匹配这些字符的字面量,需要用 \ 转义:
/\$/.test("$1"); // true,匹配字面量 $
/\(\)/.test("()"); // true,匹配字面量 ()
/\d+\.\d+/.test("3.14"); // true,匹配字面量小数点
面试易错点:在字符类
[]内,大多数元字符会自动失去特殊含义,无需转义。例如[.*+?]是一个匹配.、*、+、?中任意一个的字符类。但]、\、^(开头)、-(非末尾)仍需小心。
量词
量词用来描述前一个元素出现的次数。
| 量词 | 含义 | 等价写法 |
|---|---|---|
* | 出现 0 次或多次 | {0,} |
+ | 出现 1 次或多次 | {1,} |
? | 出现 0 次或 1 次 | {0,1} |
{n} | 恰好 n 次 | — |
{n,} | 至少 n 次 | — |
{n,m} | n 到 m 次(含两端) | — |
/a*/.test(""); // true,0 次也算
/a+/.test(""); // false,至少 1 次
/colou?r/.test("color"); // true,u 可有可无
/\d{4}/.test("2023abc"); // true,连续 4 位数字
/\d{2,4}/.test("123"); // true,2~4 位
注意
{n,m}中不能有空格,{2, 4}不会被识别为量词,而是匹配字面量字符串{2, 4}。
贪婪、懒惰、占有
量词默认是贪婪的:会尽可能多地匹配字符。
贪婪模式
"aabaa".match(/a.*a/); // ['aabaa']
// .* 尽可能多匹配,到末尾后回溯找到最后一个 a
懒惰模式
在量词后加 ? 变成懒惰模式:尽可能少地匹配。
| 贪婪 | 懒惰 |
|---|---|
* | *? |
+ | +? |
? | ?? |
{n,m} | {n,m}? |
"aabaa".match(/a.*?a/); // ['aa']
// 尽可能少匹配,找到第一个 a 后立即匹配下一个 a
经典的 HTML 标签匹配对比:
const html = '<div>a</div><div>b</div>';
html.match(/<div>.*<\/div>/); // ['<div>a</div><div>b</div>'] 贪婪
html.match(/<div>.*?<\/div>/); // ['<div>a</div>'] 懒惰
面试经典题:为什么
/".*"/.test('a="1" b="2"')会匹配到整个"1" b="2"而不是"1"?答案就是贪婪。改为".*?"即可匹配到"1"。
占有模式
在量词后加 + 变成占有模式(ES2025):尽可能多匹配,但不会回溯。一旦匹配到的字符就不会再交还。
// 占有模式:匹配失败时不回溯,整体失败
/a*+/; // 等价于 (?>a*)
占有模式主要用于性能优化,避免某些灾难性回溯。目前主流浏览器逐步支持中,了解概念即可。具体原理与回溯的关系,见 断言与回溯。
分组 ()
括号将多个元素当成一个整体,配合量词使用:
/(ab)+/.test("abab"); // true,ab 作为整体重复
/ab+/.test("abbb"); // true,只有 b 重复