以下正则表达式模式应用于非常长的字符串(60KB)时,会导致java看起来“挂起”。
.*\Q|\E.*\Q|\E.*\Q|\E.*bundle.*
我不明白为什么。
答案 0 :(得分:5)
你在那个正则表达式中有5个急切/贪婪的量词,所以你很可能会做大量的回溯。
This article详细解释了这种行为,并提出了改善正则表达式效果的建议。
在这种情况下,答案可能是将贪婪量词替换为非贪婪量词,或者更好地使用非回溯子模式。
答案 1 :(得分:4)
首先,我认为你可以简化你的正则表达式如下:
.*\Q|\E.*\Q|\E.*\Q|\E.*bundle.*
可能会成为
.*\|.*\|.*\|.*bundle.*
其次,为了回答你的问题,你的正则表达式中有这么多“。*”的事实意味着正则表达式解决方案必须有一些可能性。如果这适用于您的情况,以下可能会更快。
(?:[^\|]*\|){3}[^|]*bundle.*
通过将“。”更改为“[^ |] ”,您可以缩小引擎的焦点,因为第一个“。*”不会急切地抓住第一个“|”。
答案 2 :(得分:4)
基本上,“。*”(匹配任意数量的任何数字)意味着尝试匹配整个字符串,如果它不匹配,则返回并再试一次,等等使用其中一个并不是太多一个问题,但使用多个所需的时间呈指数增长。这是对这类事情的一个相当深入(并且更加准确)的讨论:http://discovery.bmc.com/confluence/display/Configipedia/Writing+Efficient+Regex
编辑:(我希望你真的想知道为什么)
示例源字符串:
aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks
一种看待它的方式:
该过程需要很长时间,因为.*
匹配整个源字符串(aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks
),只是发现它不以|
符号结尾,然后回溯到最后一个|
符号(...4876|2345...
)的大小写,然后尝试将下一个.*
一直匹配到字符串的结尾。
它开始寻找表达式中指定的下一个|
符号,但未找到它,然后回溯到匹配的第一个|
符号(...4876|2345...
中的符号) ,丢弃匹配并在其前面找到最接近的|
(...dfas|2936...
),以便它能匹配匹配表达式中的第二个|
符号。
然后,它会继续将.*
与2936827634876
和第二个|
与...4876|2345...
中的.*
以及下一个|
的{{1}}相匹配,只是发现你想要另一个.*\Q|\E.*\Q|\E.*\Q|\E.*bundle.*
。然后它会一次又一次地继续回溯,直到它匹配您指定的所有符号。
另一种看待它的方式:
(原始表达):
match:
any number of anything,
followed by a single '|',
followed by any number of anything,
followed by a single '|',
followed by any number of anything,
followed by a single '|',
followed by any number of anything,
followed by the literal string 'bundle',
followed by any number of anything
这大致转化为
any number of anything
问题是|
包含any number of anything that is not a '|'
符号,需要一遍又一遍地解析整个字符串,其中你的意思是.*
为了修复或改进表达式,我建议三件事:
首先(也是最重要的),将大部分“匹配任何东西”([^|]
)替换为否定字符类([^|]*\Q|\E[^|]*\Q|\E[^|]*\Q|\E.*bundle.*
),如下所示:
|
...这会阻止它一遍又一遍地匹配字符串的结尾,而是将所有非|
符号匹配到第一个不是“不是{{{ 1}}符号“(双重否定意味着直到第一个|
符号),然后匹配|
符号,然后转到下一个等等...
第二次更改(有些重要,取决于您的源字符串)应该是倒数第二次“将任意数量的任何数字匹配”(.*
)变为“懒惰” “或”不情愿“类型的”任意数量的“(.*?
)。这将使其尝试匹配任何东西,以寻找bundle
而不是跳过bundle
并匹配其余字符串,只是意识到一旦它到达那里就有更多匹配,不得不回溯。这将导致:
[^|]*\Q|\E[^|]*\Q|\E[^|]*\Q|\E.*?bundle.*
我建议的第三个更改是为了提高可读性 - 将\Q\E
块替换为单个转义符,如\|
中所示,如下所示:
[^|]*\|[^|]*\|[^|]*\|[^|].*?bundle.*
这就是表达式在内部处理的方式 - 实际上有一个函数将表达式转换为“转义\ Q和\ E之间的所有特殊字符” - \Q\E
只是一个简写,如果它不会使你的表达更短或更容易阅读,不应该使用它。周期。
否定的字符类有一个未转义的|
因为|
不是字符类上下文中的特殊字符 - 但是不要过分偏离。如果你愿意,你可以逃脱它们,但你不必这样做。
最终表达式大致翻译为:
match:
any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything that is not a '|',
followed by a single '|',
followed by any number of anything, up until the next expression can be matched,
followed by the literal string 'bundle',
followed by any number of anything
我使用的一个好工具(但要花一些钱)称为RegexBuddy - 用于理解正则表达式的伴侣/免费网站是http://www.regular-expressions.info,解释重复的特定页面是http://www.regular-expressions.info/repeat.html
RegexBuddy模仿其他正则表达式引擎,并说你的原始正则表达式需要544'步骤'来匹配,而不是我提供的版本的35'步骤'。
SLIGHTLY LONGER示例源字符串A:
aaffg, ";;p[p09978|ksjfsadfas|12936827634876|2345.4564a bundle of sticks
SLIGHTLY LONGER示例源字符串B:
aaffg, ";;p[p09978|ksjfsadfas|2936827634876|2345.4564a bundle of sticks4me
较长的源字符串'A'(在1
之前添加2936827634876
)不影响我建议的替换,但是将原始字符串增加了6个步骤
更长的源字符串'B'(在表达式末尾添加'4me')再次影响我建议的替换,但是将 48 步骤添加到原始
因此,根据字符串与上述示例的不同,60K字符串只需要544步,或者可能需要超过一百万步