为什么正则表达式*在一个地方比较慢,在其他地方比较快?
原文地址: https://cloud.tencent.com/developer/ask/36730
我在java / groovy中使用了很多正则表达式。我经常使用regex101.com。显然我也在看正则表达式的性能。
有一点我注意到,.*
正确使用可以显着提高整体性能。但是,.*
在正则表达式的结尾处简直就是性能杀手。
例如,在这个正则表达式中,所需的步数是27:
如果我先改变.*
到\s*
,这将减少到16显著所需的步骤:
但是,如果我第二个变化.*
来\s*
,它没有任何进一步降低步骤:
问题:
- 有什么区别?
- 本网站提供的步骤计数器是否真的给出了正则表达式的性能问题?
写回答关注邀请回答
提问于 2018-01-25
为什么正则表达式*在一个地方比较慢,在其他地方比较快?
写回答关注
2 个回答
用户回答回答于 2018-01-26
以下是从调试器输出。
性能差异的主要原因是.*
会消耗一切,直到字符串结尾(除了换行符)。将继续,迫使正则表达式回溯(如第一个图片中所见)。
模式结尾的表现同样出色的原因在于\s
,.*
如果没有别的东西可以匹配(除了WS),那么greedy模式与消耗空白就没有区别了。
如果你的测试字符串没有以空格结尾,性能就会有所不同,就像你在第一个模式中看到的一样 - 正则表达式将被迫回溯。
如果以除空白外的其他内容结束,则可以看到性能差异:
^myname.*mahesh.*hiworld
更好:
^myname.*mahesh\s*hiworld
更好:
^myname\s*mahesh\s*hiworld
用户回答回答于 2018-01-26
正则表达式引擎与*
quantifier(又名greedyquantifier)一起工作的方式是匹配输入中的所有内容:
由于.
匹配任何东西(几乎),遇到之后的第一个状态.*
是将指针移动到输入的结尾处,然后开始通过输入一个字符一次一个字符地尝试下一个字词,直到匹配。
而且\s*
,只有空白被消耗,所以指针最初被移动到你想要的地方 - 不需要跟踪下一个术语的回溯。
你应该使用.*?
,这将消耗一个字符,直到下一个匹配,这应该有相同的时间复杂性\s*
,但是稍微更有效,因为不需要检查当前字符。
\s*
并且.*
在表达式的末尾将执行类似的操作,因为两者都将消耗匹配的f输入的结尾处的所有内容,这使得两个表达式的指针是相同的位置。