Javascript 你能解释一下为什么这些正则表达式很慢吗
最近我在代码中发现了以下正则表达式。由于它所检查的字符串相当大,它冻结了浏览器。在运行一些实验时,我使用以下代码测量了时间Javascript 你能解释一下为什么这些正则表达式很慢吗,javascript,regex,Javascript,Regex,最近我在代码中发现了以下正则表达式。由于它所检查的字符串相当大,它冻结了浏览器。在运行一些实验时,我使用以下代码测量了时间 var s='Lorem ipsum door sit amet,rhoncus nam sem feugiat,vel vel,viverra ultrices interdum。沃里克·康格。在设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面。白骨木兰(Magnis metus nulla
var s='Lorem ipsum door sit amet,rhoncus nam sem feugiat,vel vel,viverra ultrices interdum。沃里克·康格。在设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面。白骨木兰(Magnis metus nulla mauris dictum ligula),白骨木兰(odio facilisis nullam laoreet)。阿利奎姆·汀西德·埃尼坐在我旁边。二者缺一不可,侵权行为造成的设备损坏。埃吉特是我的朋友。这是一个令人信服的事实,或者是一个与欧盟孕妇有关的事实。伊普苏姆万岁,佩纳蒂布斯万岁,波苏尔万岁,欧盟万岁,自由精英万岁。自由的布兰迪特·马蒂斯·米·萨皮恩,艾库利斯·维西·康瓦利斯,在自由的世界里,在前庭里,是一种粗糙的元素;
对于(变量i=0;i<3;i++){
s+=s;
}
开始=新日期().getTime();
s、 匹配(/AAA/i)
停止=新日期().getTime();
log(“AAA take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/BBB/i)
停止=新日期().getTime();
log(“BBB take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/CCC/i)
停止=新日期().getTime();
log(“CCC take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/.*(AAA | BBB | CCC)。*/i)
停止=新日期().getTime();
log(“Combined take”+(stop-start)+“ms”)
您在以前的测试中没有包含*
,导致您的匹配很快返回false
在这里,您可以看到每个测试运行大约需要30ms
全局仍只需40ms
除此之外,使用s.match(/AAA | BBB | CCC/i)
会更快
var s='Lorem ipsum door sit amet,rhoncus nam sem feugiat,vel vel,viverra ultrices interdum。沃里克·康格。在设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面,设备方面。白骨木兰(Magnis metus nulla mauris dictum ligula),白骨木兰(odio facilisis nullam laoreet)。阿利奎姆·汀西德·埃尼坐在我旁边。二者缺一不可,侵权行为造成的设备损坏。埃吉特是我的朋友。这是一个令人信服的事实,或者是一个与欧盟孕妇有关的事实。伊普苏姆万岁,佩纳蒂布斯万岁,波苏尔万岁,欧盟万岁,自由精英万岁。自由的布兰迪特·马蒂斯·米·萨皮恩,艾库利斯·维西坐在康瓦利斯,在自由的世界里最美,在前庭的世界里最美
对于(变量i=0;i<3;i++){
s+=s;
}
开始=新日期().getTime();
s、 匹配(/.*AAA.*/i)
停止=新日期().getTime();
log(“AAA take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/.*BBB.*/i)
停止=新日期().getTime();
log(“BBB take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/.*CCC.*/i)
停止=新日期().getTime();
log(“CCC take”+(停止-启动)+“ms”)
开始=新日期().getTime();
s、 匹配(/.*(AAA | BBB | CCC)。*/i)
停止=新日期().getTime();
log(“Combined take”+(stop-start)+“ms”)
该/AAA/i
,/BBB/i
,/CCC/i
与/.*(AAA | BBB | CCC)的区别不仅在于交替捕获组的使用,而且在于*/code>模式
/.*(AAA | BBB | CCC)。*/i
模式会减慢匹配速度,因为第一个*
是贪心点模式。其工作方式如下:
- 它会抓取它能匹配的所有字符(除换行字符以外的0个或多个CHSR),因此,基本上,它会抓取整个行
- 然后,它必须匹配
AAA
或BBB
或CCC
,因此回溯开始:引擎从匹配结束时产生一个字符,并尝试匹配其中一个替代项
- 如果缺少替代项,或者它们位于很长字符串的开头,则引擎将徒劳地尝试容纳字符串的一部分以进行捕获组匹配
请参见可视化回溯步骤
因此,要确定一行/字符串是否包含这三个选项中的任何一个,最好的方法就是使用
/(?:AAA|BBB|CCC)/i
因为这个正则表达式可以找到部分匹配(它不需要像Java的string#match()
那样的完整字符串匹配)
如果您需要在多行字符串中查找具有其中一个备选项的行,最好逐行处理(例如,使用\n
拆分),然后使用.filter(x=>/(?:AAA | BBB | CCC)/i.test(x))
注意(?:…)
是一个不创建子匹配的非捕获组,由于引擎不必为捕获分配额外的内存,因此开销较小。只需使用s.match(/(?:AAA | BBB | CCC)/i)
查找3个备选方案中的任何一个,如果需要从多行文本中获取整行,首先用\n
将其拆分,并使用此正则表达式进行过滤。感谢Bram Vanroy-您能解释一下在正则表达式开始时“:”的作用吗?重点是*
之前的(AAA | BBB | CCC)
会导致大量回溯。非捕获组,(?:…)
,比捕获组快,因为引擎没有为子匹配分配内存。我发布了答案,解释了为什么它慢。感谢Wiktor Stribiżew,被接受-有趣的是,当我打开该链接时,在线调试器冻结(无法向下滚动);-)当我减少了测试字符串的长度时,它起了作用。我想为了我想要的东西(只要检测到字符串中有任何单词),我可以从组合正则表达式中删除“*”,它将与单个正则表达式一样快one@szydan真的,谢谢。我接受了Wictor的答案,因为它包含一个解释+到在线调试器的链接