正则表达式的进阶用法

本文将介绍几种正则表达式的进阶用法。

前瞻、后顾、负前瞻、负后顾(positive/negative lookahead/lookbehind)

当我们想要匹配一段“后面为特定内容”的文本,但又不想匹配“特定内容”时,可以使用前瞻(positive lookahead)。比如:

1
foo(?=bar)

只会匹配后面是 barfoo

1
2
3
[foo]bar
foobaz
fooqux

当我们想要匹配一段“后面不为特定内容”的文本时,可以使用负前瞻(negative lookahead)。比如:

1
foo(?!bar)

只会匹配后面不是 barfoo

1
2
3
foobar
[foo]baz
[foo]qux

与前瞻、负前瞻相对应的是后顾、负后顾(positive lookbehind/negative lookbehind)。它们分别匹配“前面为特定内容”和“前面不为特定内容”的文本。比如:

1
(?<=bar)foo

只会匹配前面是 barfoo

1
2
3
bar[foo]
bazfoo
quxfoo

而:

1
(?<!bar)foo

只会匹配前面不是 barfoo

1
2
3
barfoo
baz[foo]
qux[foo]

原子组(atomic group)

原子组(atomic group)和普通组(group)类似,唯一的区别是,一旦原子组中的一个分支匹配成功,它就不允许回溯尝试其他分支。比如,普通组:

1
(foobar|foo)bar

可以匹配:

1
2
[foobar]
[foobarbar]

而原子组:

1
(?>foobar|foo)bar

只会匹配:

1
2
foobar
[foobarbar]

考虑文本为 foobar 时的情况,正则表达式引擎会挨个尝试组内的分支,首先是 foobar 分支,但这样就没有剩余文本匹配最后的 bar 了。对于普通组,引擎可以回溯尝试 foo 分支,最终匹配成功;但对于原子组,引擎不能回溯,最终匹配失败。

为什么要有原子组呢?

举个例子,用 (a+)+ 作为表达式,在匹配 aaaaaaaaaa! 的输入时,虽然最终会失败,正则表达式引擎还是会反复尝试不同组合,导致匹配时间急剧增长、CPU 占用升高、程序卡顿。这种情况通常称为“灾难性回溯”。原子组可以通过减少不必要的回溯,来避免这种情况。

非捕获组(non-capturing group)

对于普通组,除了包含多个分支外,正则表达式引擎还会“记住”匹配到的内容。在用 sed 等工具替换时,可以用 \1\2 等引用匹配内容。如果只需要多个分支、不需要“记住”匹配内容,可以使用非捕获组(non-capturing group)。比如:

1
(?:foo|bar)

总结

正(Positive) 负(Negative)
前瞻(Lookahead) (?=...) (?!...)
后顾(Lookbehind) (?<=...) (?<!...)
语法
原子组(Atomic group) (?>...)
非捕获组(Non-capturing group) (?:...)

在线工具

最后推荐一个非常方便的在线工具:https://regex101.com/

参考资料


正则表达式的进阶用法
https://tomzhu.site/2026/07/19/正则表达式的进阶用法/
作者
Tom Zhu
发布于
2026年7月19日
许可协议