Java中String.matches匹配字符串耗时过长问题排查
你的正则表达式为啥会陷入无限运行?
这个问题典型得很——你碰到了正则表达式的灾难性回溯,再加上正则没锚定结尾,双重buff叠满,直接让引擎原地“卡壳”。
先拆解下你的正则:
String pattern = "^[a-zA-Z]+([\\s]*[\\w]*)*\\([\\w]+\\)";
咱们把它拆成几块看:
^[a-zA-Z]+:要求开头是至少一个字母,这部分没问题([\\s]*[\\w]*)*:这就是坑的核心!子组里的\s*(0或多个空格)和\w*(0或多个单词字符)本身都是可选的,然后整个子组又被*修饰(可以重复0或多次)。这种“可选元素套可选重复”的结构,会让正则引擎在匹配失败时产生无数种回溯路径\\([\\w]+\\):要求匹配括号包裹的单词字符,这部分本身没问题,但你没加结尾锚定$,意味着正则只要在字符串里找到符合的片段就算匹配,而不是要求整个字符串以这个结尾
为啥text2会触发无限运行?
当你用"Asdadasdasd sadsdsad sdasd (s) sdsd"测试时:
- 正则先匹配开头的字母,然后用
([\\s]*[\\w]*)*匹配到(s)前面的所有内容,接着匹配(s) - 这时候发现字符串后面还有
sdsd,而你的正则没加$,所以引擎会想:“是不是我前面的匹配范围不对?要不要把([\\s]*[\\w]*)*的匹配长度缩短一点,让(s)后面没有内容?” - 但因为
([\\s]*[\\w]*)*的结构太灵活了——比如可以把某个\w*少匹配一个字符,或者把一段内容拆成多个子组来匹配,引擎会不断尝试这些可能的组合,而这些组合的数量是指数级的,直接导致引擎陷入无限循环,看起来就像程序“卡死”了
怎么修复?
解决方法很简单,两步走:
- 干掉嵌套的可选重复:把
([\\s]*[\\w]*)*改成[\\s\\w]*——因为\s*加\w*的组合,本质上就是匹配任意数量的空格或单词字符,直接合并成一个字符类加*,避免嵌套的重复结构 - 锚定结尾:在正则最后加上
$,明确要求整个字符串必须以括号内容结尾,这样引擎就不会在字符串中间瞎找匹配了
修改后的正则应该是这样:
String pattern = "^[a-zA-Z]+[\\s\\w]*\\([\\w]+\\)$";
这样不管是text还是text2,都会快速返回结果:text匹配成功,text2因为后面多了sdsd,会直接返回false,再也不会卡死了
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

