正则表达式反向引用

在同一模式中重用捕获组匹配文本的功能。使用 \1、\2 等引用。

正则表达式反向引用是在同一正则表达式模式中使用 \1、\2 等记法重用捕获组 () 匹配文本的功能。第一个捕获组用 \1 引用,第二个用 \2,按分组出现的顺序分配编号。

反向引用的典型用例是检测重复的单词。模式 (\w+)\s+\1 可以匹配"the the"这样连续出现相同单词的情况。HTML 的标签匹配 <(\w+)>.*?</\1> 也常在介绍反向引用时被引用,但认为靠它就能验证开始标签与结束标签的对应关系是错误的。反向引用看的是作为字符串的一致,因此 <B>x</b> 作为 HTML 是同一个标签,却只因大小写不同而判为不匹配。对于 <div><div>a</div></div>,匹配会在内层的结束标签处被截断,于是匹配到的是对应关系并不成立的范围。有嵌套的结构无法用正则表达式表达,因此解析 HTML 要使用解析器。

在替换操作中,可以使用 $1、$2 (在某些语言中是 \1、\2) 引用捕获组的内容。例如用 (\d{4})-(\d{2})-(\d{2}) 匹配日期格式,再用 $3/$2/$1 替换,就能把"2025-01-15"转换为"15/01/2025"。在文本的整理与转换上,它是一件强有力的工具。不过,如果在替换字符串中紧接编号引用之后再写数字,解释就会出现摇摆。在 JavaScript 中,对只有 2 个分组的模式写 $10,展开的不是并不存在的第 10 组,而是第 1 组加上字符"0"。在后面紧跟数字的场合,使用命名引用 $<name> 就不会留下歧义。

使用命名捕获组 (?<year>\d{4}) 后,可以用 \k<year> 按名称进行反向引用。它比按编号引用可读性更高,也能防止因增删分组而造成的编号错位。JavaScript 从 ES2018 开始支持命名组。

使用反向引用时需要注意对性能的影响。包含反向引用的模式无法转换为有限自动机,因此引擎只能依赖回溯来搜索。一旦与量词组合,搜索量就会随输入长度急剧膨胀,若把外部给来的字符串作为处理对象,还存在响应回不来的危险 (ReDoS)。此外,反向引用超出了正则语言的能力范围,因此部分正则表达式引擎 (如 RE2) 并不支持它。

在与字符计数的关联上,使用反向引用的正则表达式对检测重复模式很有用。它可以用于发现文本中重复的字符串以分析冗余,或检测复制粘贴导致的意外重复。

分享这篇文章