正则表达式 后面不要包含内容 的写法不生效?

2019-04-08 08:09:39 +08:00
 xiangyuecn

昨天学会了如何编写 不要包含指定的字符串内容 的正则表达式写法。

特意写了一篇文章记录了一下:

https://www.cnblogs.com/xiangyuecn/p/10668378.html

里面有一个一直困扰的问题,假设:

提取<abcdef>\n<abczzz>中首个不包含 def 结尾的 abc 标签,只知道def,不知道zzz


很简单能写成( v2 页面浏览器控制台测试,但 not only javascript ):

/<abc(?!def).+>/.exec("<abcdef>\n<abczzz>")


但往往我们不能写死abc,顺理成章的就写成了:

/<.+(?!def).+>/.exec("<abcdef>\n<abczzz>")


上面这个不生效,昨天刚发现写成这样就可以了:

/<(?:.(?!def))+>/.exec("<abcdef>\n<abczzz>")


不明白为什么.+(?!def)不会生效,前瞻不会和前面的+、*、{}起作用吗?

3049 次点击
所在节点    程序员
16 条回复
xiangyuecn
2019-04-08 08:24:35 +08:00
又发现另一种有效写法:
/<(?!.+def).+>/.exec("<abcdefzzz>\n<abczzz>")

可能是结尾的.+导致的不能匹配,但这样写还是不行:
/<.+(?!def)zzz>/.exec("<abcdefzzz>\n<abczzz>")
frank611
2019-04-08 08:26:55 +08:00
def 后面的.+是不对的,+是必须匹配一个字符或者更多
binux
2019-04-08 08:27:32 +08:00
/<(.+)(?!def)(.+)>/.exec("<abcdef>\n<abczzz>")
你就知道为什么了
xiangyuecn
2019-04-08 08:31:39 +08:00
@binux 还是不明白,第一个分组捕获到 abcde,预期想要是 abc,前瞻不会和前面的+、*、{}起作用吗?
binux
2019-04-08 08:36:41 +08:00
@xiangyuecn #4 别整那么多名词,Matches 'x' only if 'x' is not followed by 'y',你自己看看"第一个分组捕获到 abcde"还 followed by 'y' 了吗
zhyl
2019-04-08 08:36:53 +08:00
.+ 贪婪匹配
labnotok
2019-04-08 09:23:20 +08:00
貌似 js 不支持预查吧,只能手动分组
xiangyuecn
2019-04-08 09:27:46 +08:00
@binux 还是不太明白,第一个分组的确是捕获到了`abcde`呀,

照着你的思路找到 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions 的解释中有个例子:
> >>>>>
Matches 'x' only if 'x' is not followed by 'y'. This is called a negated lookahead.

For example, /\d+(?!\.)/ matches a number only if it is not followed by a decimal point. The regular expression /\d+(?!\.)/.exec("3.141") matches '141' but not '3.141'.
> >>>>>

发现一个现象:

符合预期:/(\d+)(?!\.1)/.exec("123.141")
符合预期:/([^.]+)(?!\.1)/.exec("123.141")
无效:/(.+)(?!\.1)/.exec("123.141")

难以理解,最后一个就是我的写法,咳。。。
araraloren
2019-04-08 09:29:27 +08:00
js 的正则有些特性不支持: https://www.regular-expressions.info/javascript.html
xiangyuecn
2019-04-08 09:30:37 +08:00
noqwerty
2019-04-08 09:37:16 +08:00
你这个非要用正则的话感觉只能 <(?!.+def>).+> ,你的写法里 <.+(?!def).+> 第一个.+是贪婪的,会把 def 也包括进去。
noqwerty
2019-04-08 09:38:06 +08:00
另外测试正则的时候 https://regex101.com/ 很好用
binux
2019-04-08 09:38:16 +08:00
@xiangyuecn #8
/<.+(?!def).+>/.exec("<abcdef>\n<abczzz>")
第一个 .+ 匹配到了 abcde,之后是 f,不是 def,第二个 .+ 匹配 f,符合正则

/(.+)(?!\.1)/.exec("123.141")
第一个 .+ 匹配到了 123.141 ,之后是 EOF 不是 .1,也符合正则
xiangyuecn
2019-04-08 10:08:46 +08:00
@binux 嗯,原来如此呀,分解一下豁然开朗,哈哈

意思就是前瞻只能作用于+贪婪匹配到的最后一个字符,并不能阻止+对最后一个字符之前的所有字符进行贪婪匹配。

/(\d+)(?!\.1)/.exec("123.141") 目测是这样的:
> 123:\d+贪婪匹配到.为止
> 12:发现 123.1 不符合(?!\.1),后退一位
> 没有表达式了,返回 12

/(.+)(?!\.1)/.exec("123.141") 目测是这样的:
> 123.141:.+贪婪匹配到结尾
> 123.141 : 符合(?!\.1)
> 没有表达式了,返回 123.141

/(.(?!\.1))+/.exec("123.141") 目测是这样的:
> 1:.匹配到新的一位
> 1:123 符合(?!\.1)
> 12:.匹配到新的一位
> 12:123.符合(?!\.1)
> 123:.匹配到新的一位
> 12:发现 123.1 不符合(?!\.1),后退一位,并退出循环
> 没有表达式了,返回 12

如果要对每个字符进行前瞻检查,唯有最后一种写法比较好理解。
zhyl
2019-04-08 11:03:55 +08:00
试试这个 `<\w+(?!def)(?<!def)\b>`
xiangyuecn
2019-04-08 11:05:53 +08:00
@zhyl 尽量不要用后瞻,难移植

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/552813

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX