正则表达式中的命名组与非捕获组
括号有两项工作:分组和捕获。本文介绍非捕获组 (?:…) 如何只分组不捕获,命名组 (?<year>…) 如何让捕获内容更易读,如何用 \k<name> 引用它们,以及 Python 里的写法。

· 阅读约 3 分钟
/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/
抓住
- 2026-10-13
- 1999-12-31
- 2000-01-01
- 2026-02-28
放过
- 26-10-13
- 2026/10/13
- 2026-1-3
- 13-10-2026
正则表达式里的括号同时做两件事。它们负责分组,让量词或 | 作用于括号里的全部内容。它们也负责捕获,把匹配到的文本保存下来,供你之后使用。
很多时候你只需要其中一项。本文介绍把这两项工作拆开的两种分组:非捕获组和命名组。每个示例都可以交互,修改正则就能看到各行的变化。
01普通分组会捕获
普通分组会保存它匹配到的内容,并按左括号出现的位置从 1 开始编号:
const m = "2026-10-13".match(/(\d{4})-(\d{2})-(\d{2})/);
m[0]; // "2026-10-13", the whole match
m[1]; // "2026"
m[2]; // "10"
m[3]; // "13"
在正则改动之前,这一切都没问题。可一旦在开头附近加一个分组,后面的编号就全部顺延,m[2] 会悄悄变成别的东西。
02非捕获组:(?:…)
需要分组、但不关心文本时,用 (?: 代替 (。下面这个分组存在的唯一目的,是让 + 重复“一个单词加一个空格”:
它的匹配结果和 (\w+ )+\w+ 完全相同,区别只在于你拿回来的东西:没有需要编号的捕获,改动正则时也不会有编号顺延。它还稍微省一点开销,因为引擎不必保存文本。
一个好习惯:凡是之后不读取的分组,都用 (?:…)。这样剩下的编号组或命名组,就都是真正有意义的。
03命名组:(?…)
给分组起个名字,然后按名字而不是按编号读取它:
/^(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})$/应当匹配2/2
- 2026-10-13
- 1999-12-31
不应匹配3/3
- 26-10-13
- 2026/10/13
- 2026-1-3
const { year, month, day } =
"2026-10-13".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/).groups;
year; // "2026"
命名组同样有编号,所以无论哪种写法,m[1] 都是年份。名字只是让代码在正则改动后依然有效。
在替换中也能用,写作 $<name>:
"2026-10-13".replace(
/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/,
"$<d>/$<m>/$<y>",
); // "13/10/2026"
04反向引用:\k
反向引用匹配的是某个分组之前匹配过的同一段文本。对于命名组,写法是 \k<name> 而不是 \1:
这种写法一看就知道是什么意思,\1 永远做不到这一点;而且即使你在前面加了分组,它依然有效。
05Python 的写法不同
Python 最早支持命名组,写法里带一个 P:
import re
m = re.match(r"(?P<year>\d{4})-(?P<month>\d{2})", "2026-10")
m.group("year") # "2026"
m.groupdict() # {"year": "2026", "month": "10"}
re.sub(r"(?P<y>\d{4})-(?P<m>\d{2})", r"\g<m>/\g<y>", "2026-10") # "10/2026"
re.match(r"(?P<q>[\"']).*(?P=q)$", '"hi"') # backreference by name
所以同一个概念,在 JavaScript 里写作 (?<name>…) 和 \k<name>,在 Python 里则是 (?P<name>…)、(?P=name) 和 \g<name>。非捕获组 (?:…) 在哪里都一样。
06重复的分组只保留最后一次
还有一个意外:放在量词里的分组只保留最后一次重复。
"abc".match(/(\w)+/)[1]; // "c", not ["a", "b", "c"]
如果你需要每一次重复,请改为对内部的正则使用 g 修饰符加 matchAll(或 re.findall)。
07试一试
引号必须成对。用一个分组,再加一个指回它的引用:
抓住用成对引号包起来的文本:"…" 或 '…'。
目标 · 抓住 0/5
- "hi"目标
- 'hi'目标
- "it's"目标
- 'say "no"'目标
- ""目标
无辜词 · 误抓 0
- "hi'无辜
- 'hi"无辜
- hi无辜
- "hi无辜
- hi"无辜
输入正则——棋盘会实时更新。