如何用正则匹配首尾字符间不含指定列表任意子串的字符串
问题根因分析
你写的原正则1(?!this|is|a|demo).*2存在三个核心问题:
- 负向前瞻仅在匹配到开头的
1后的零宽位置校验一次,不会对1到2之间的全段内容做遍历检查,根本无法实现“拦截中间任意位置出现禁止子串”的效果。 - 因为校验范围只覆盖1后面的紧邻位置,只要这个位置右侧的内容开头匹配到禁止串,就会直接拒绝后续匹配,完全不考虑禁止串后面的内容。比如
1demoo2的1后面右侧前四个字符正好是demo,直接被前瞻拦截;如果正则分支的匹配顺序存在优先级问题,就会出现两个结构相似的字符串被区别对待(比如能匹配1demo12、不能匹配1demoo2)的异常情况。 - 从你给出的测试用例来看,你的实际需求和最初的描述存在偏差:如果严格要求“1和2之间不能包含任意禁止子串”,那么
1demoo2、1demo12本身就包含demo这个禁止子串,应当被拦截,不可能进入需要匹配的范围。结合测试用例判断,你实际要拦截的是「禁止列表的字符串作为1和2之间的完整内容存在」的场景——也就是只有当1后面直接接禁止串、禁止串后面直接是结尾的2时才拒绝,只要中间多了任意其他字符,哪怕出现禁止串的片段,也允许匹配。
对应不同需求的正确正则
场景1:严格匹配“禁止任意位置出现禁止子串”
如果真的需要实现“1和2之间的所有位置,都不能出现this/is/a/demo任意子串”的要求,正则需要把负向前瞻嵌入到遍历循环中,每走过一个字符都做一次校验:1(?:(?!this|is|a|demo).)*2
注意:这个正则会拦截1demoo2、1demo12,因为两个字符串的中间段都包含demo子串,和你给出的测试用例不兼容。
场景2:匹配你给出的测试用例规则
结合你的测试判定要求(拦截1this2、1is2、1a2、1demo2,放行1demoo2、1demo12),正确的正则为:1(?!(?:this|is|a|demo)2$).+2
逻辑说明:
- 开头匹配起始字符
1 - 紧跟负向前瞻:校验当前位置右侧的内容是否是“禁止串 + 结尾的2”,如果是就直接终止匹配(也就是拦截掉禁止场景的四个字符串)
- 中间匹配非空的任意字符,直到遇到结尾的
2
测试验证:
- 四个禁止场景的字符串,都会被前瞻逻辑拦截,不产生匹配
1demoo2、1demo12因为禁止串后面不是直接跟结尾的2,会正常通过匹配
内容的提问来源于stack exchange,提问作者distinct
相关产品推荐
相关产品推荐

