正则表达式:先行与后行断言
断言(lookaround)用于检查匹配位置前后是否满足某个条件,但断言中的文本不会包含在最终匹配结果里。它适合“提取某段内容、但要求它有特定上下文”的场景。
兼容性
正向与负向先行断言支持较广;RE2 明确不支持 lookaround。Python
re的后行断言内部必须匹配固定长度,其他引擎对可变长度和分支长度有各自限制。JavaScript 支持还取决于实际运行时版本。遇到兼容性问题时,可改用捕获组并从结果中读取目标部分。
1. 正向先行断言
功能说明
正向先行断言 (?=...) 要求当前位置后面的文本能匹配括号中的模式,但不把这段后续文本纳入结果。它常用于“匹配某数值,但要求后面跟着某单位”。
语法格式
目标(?=后续条件)简单示例
\d+(?=元)测试文本:价格 120元,数量 3件
匹配结果说明
结果是 120,而不是 120元。元 只作为必须满足的后续条件,不会成为匹配结果的一部分;3 后面是 件,因此不匹配。
2. 负向先行断言
功能说明
负向先行断言 (?!...) 要求当前位置后面的文本不能匹配指定模式。它适合排除某些后缀、关键字或格式,但要保证断言放在正确的位置,避免只排除了部分文本。
语法格式
目标(?!不允许的后续条件)简单示例
\b\w+\.(?!exe\b)\w+\b测试文本:readme.txt setup.exe image.png
匹配结果说明
结果是 readme.txt 和 image.png。在点号之后,断言要求扩展名不能是完整的 exe;因此 setup.exe 被排除。这个规则只检查扩展名结构,不代表文件一定安全。
3. 正向后行断言
功能说明
正向后行断言 (?<=...) 要求当前位置前面的文本满足某个模式,但不把前面的文本纳入结果。它常用于从带前缀的字段中只提取主体内容。
语法格式
(?<=前置条件)目标简单示例
(?<=\$)\d+测试文本:$120 €80 60
匹配结果说明
结果是 120,因为它紧接在美元符号 $ 之后。80 和 60 不满足前置条件。若运行环境不支持后行断言,可先匹配 \$(\d+),再读取第一个捕获组。
4. 负向后行断言
功能说明
负向后行断言 (?<!...) 要求当前位置前面的文本不能匹配指定模式。它适合匹配“不带某前缀”的内容;与正向后行断言一样,需注意引擎对后行长度与语法的限制。
语法格式
(?<!不允许的前置条件)目标简单示例
(?<!\$)\b\d+\b测试文本:$120 80 €60
匹配结果说明
结果是 80 与 60,而 $120 中的 120 因为前一个字符是 $ 而不匹配。这个模式只检查紧邻位置;若业务规则是“整段文本中不能含货币符号”,需要写更完整的规则或先做结构化解析。
5. 断言与捕获组的选择
功能说明
断言适合“检查但不返回”的上下文;捕获组适合“匹配并取出”的上下文。两种写法有时都能完成任务,选择时优先考虑兼容性和后续代码是否容易读取结果。
语法格式
断言:(?<=前缀)目标
捕获:前缀(目标)简单示例
(?<=ID:)\d+
ID:(\d+)测试文本:ID:42
匹配结果说明
第一个模式的整体匹配就是 42;第二个模式的整体匹配是 ID:42,但第一个捕获组为 42。第二种写法在不支持后行断言的环境中更通用。
练习与自测
- 用先行断言从
120ms 80s中只提取后接ms的数字;再改用捕获组实现。 - 在 Python 中尝试编译
(?<=a+)b,记录异常;解释“固定长度后行”的含义。 - 将“排除
.exe后缀”的规则写出readme.txt、archive.exe、archive.exe.bak三个测试,先定义你要排除哪一种。 - 为什么断言不消耗文本,却仍可能参与回溯?
- 若目标引擎是 RE2,应如何把依赖 lookaround 的提取拆成普通匹配加程序判断?
上一节:分组、分支与反向引用|下一节:标志与替换。