Pattern.matches() 给出了 StackOverflowError

2022-09-02 22:07:42

我正在使用java的Pattern.matches将数据块与正则表达式进行匹配。数据块可以是单行或多行。问题是,一旦我的数据超过15行(通常超过17-18行),我就开始得到stackoverflowerror。对于少于15行的数据,正则表达式工作正常。

正则表达式的格式如下:
域名 -> 空格 -> , -> 空格 ->号 -> 空格 -> , -> 空格 -> 号 -> 换行符

String regex = "^(([a-zA-Z0-9][a-zA-Z0-9\\-]*\\.)+([a-zA-Z]{2,})\\s*,\\s*\\d+\\s*,\\s*\\d+(\\r?\\n)?)+$";

我用来测试这个正则表达式的数据块是这个

abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456
abc.com, 123, 456

这是代码:

String regex = "^(([a-zA-Z0-9][a-zA-Z0-9\\-]*\\.)+([a-zA-Z]{2,})\\s*,\\s*\\d+\\s*,\\s*\\d+(\\r?\\n)?)+$";
boolean valid = Pattern.matches(regex, data); //fails here

答案 1

我无法告诉您此错误的原因;正则表达式本身很好,不会受到灾难性回溯或任何其他明显错误的影响。

也许您可以通过使用所有格量词(而不是 ,而不是 、而不是 等)来减少正则表达式引擎保存的回溯位置的数量。另外,您不需要捕获组(谢谢Thomas),因此我已将它们更改为非捕获组:+++*+*{2,}+{2,}

"(?:(?:[a-zA-Z0-9][a-zA-Z0-9-]*+\\.)++([a-zA-Z]{2,}+)\\s*+,\\s*+\\d++\\s*+,\\s*+\\d++(\r?+\n)?+)++"

这不会改变正则表达式的行为(除了删除不必要的锚点,因为你正在使用),但也许它有助于避免StackOverflows。我没有安装Java SDK,所以我不能自己测试它。Pattern.matches()


答案 2

您可以尝试使用原子组 () 来防止回溯:(?>expression)

这是一个测试,使用正则表达式的1000行块失败,但现在成功了(需要一段时间,因此我只用5000 20000:)测试):

String regex = "(?>(?>[a-zA-Z0-9][a-zA-Z0-9\\-]*\\.)+(?>[a-zA-Z]{2,})\\s*,\\s*\\d+\\s*,\\s*\\d+(?>\\r?\\n)?)+";

StringBuilder input = new StringBuilder();

for( int i = 0; i < 1000000; ++i) {
  input.append("abc.com, 123, 456\n");
}

Pattern p = Pattern.compile( regex );
Matcher m = p.matcher( input );

System.out.println(m.matches());

因此,毕竟,它可能仍然是一个回溯问题。

更新:只是让该测试运行20000行,仍然没有失败。这至少是以前的20倍。:)

更新2:再次查看我的测试,我发现了慢的部分,字符串串联。(o..O).我已经更新了测试并使用了100万行,仍然没有失败。:)