Java中长字符串的正则表达式模式匹配性能
我有一个正则表达式,它在找到匹配项时工作得很好(500纳秒),但在没有匹配项时需要很多时间(超过3秒)。我怀疑这可能是因为回溯。我尝试了一些选项,比如根据一些文档将Java中长字符串的正则表达式模式匹配性能,java,regex,Java,Regex,我有一个正则表达式,它在找到匹配项时工作得很好(500纳秒),但在没有匹配项时需要很多时间(超过3秒)。我怀疑这可能是因为回溯。我尝试了一些选项,比如根据一些文档将*转换为(.*),但没有帮助 输入:非常长的字符串-在某些情况下为5k字符 要匹配的正则表达式:*substring1.*substring2. 我正在预编译模式并重新使用matcher,我还可以尝试什么 下面是我的代码片段——我将使用数百万个不同的输入字符串调用此方法,但只使用少数几个正则表达式模式 private static H
*
转换为(.*)
,但没有帮助
输入:非常长的字符串-在某些情况下为5k字符
要匹配的正则表达式:*substring1.*substring2.
我正在预编译模式并重新使用matcher,我还可以尝试什么
下面是我的代码片段——我将使用数百万个不同的输入字符串调用此方法,但只使用少数几个正则表达式模式
private static HashMap<String, Pattern> patternMap = new HashMap<String, Pattern>();
private static HashMap<String, Matcher> matcherMap = new HashMap<String, Matcher>();
使用String.indexOf()。您可以将问题重新编码为:
public static boolean containsStrings(String source, String string1, String string2) {
long pos1, pos2;
pos1 = source.indexOf(string1);
if(pos1 > -1) {
pos2 = source.indexOf(string2,pos1 + string1.length);
if(pos2 > pos1 && source.indexOf(string1,pos2 + string2.length) < -1) {
return true;
}
}
return false;
}
public静态布尔值包含字符串(字符串源、字符串string1、字符串string2){
长pos1,pos2;
pos1=source.indexOf(string1);
如果(位置1>-1){
pos2=source.indexOf(string2,pos1+string1.length);
if(pos2>pos1&&source.indexOf(string1,pos2+string2.length)<-1){
返回true;
}
}
返回false;
}
请注意,我的解决方案不处理string2
包含在string1
中的情况,如果是这种情况,您需要将其添加到逻辑中。如果使用indexOf()
,您可以验证模式是否匹配:
当正则表达式不匹配时,您将得到灾难性的回溯。事实上,您的模式可能会进行很多回溯,即使在有匹配的情况下也是如此。*
将耗尽整个字符串,然后需要返回,不情愿地返回字符
如果您的字符串看起来像:substring1 substring2…….50000个字符……
,那么您将使用lazy*?
获得更好的性能。请注意,(.*)
与*?
不同
正则表达式的性能取决于子字符串是什么以及它们与什么匹配。如果字符串看起来像:substring1…….50000个字符。。。。。。子字符串2
,则您将使用现有的*
获得更好的性能。正如您所暗示的,您的正则表达式存在一个称为灾难性回溯的问题。基本上,第一个*
将匹配整个字符串,然后回溯到子字符串1
匹配为止。这将在子字符串2
中重复。因为子字符串2
失败,第二个*
将需要找到另一个子字符串2
开始匹配的位置,然后它将再次失败。每次substring1
匹配时,我们都需要检查substring2
可能匹配的每个位置
您已经在使用模式.find()
,因此可以省略开头和结尾*
。然后,将内部的*
更改为*?
可以通过将贪婪匹配器变为懒惰匹配器来提高性能
这将生成:substring1.*?substring2
^((?!substring1)。)*substring1((?!substring2)。*substring2.*\Z
应该这样做,因为一个字符串多次包含一个子字符串,但不是按顺序同时包含两个子字符串,它不会回溯到令人厌恶的地方。如果不需要匹配器在输入结束时结束,则可以将。*?\Z放在末尾。您的目标是什么?您需要使用正则表达式吗?请出示您的code@Pshemo-是的,我必须使用正则表达式。是否有需要的原因。*前后?如果使用find()而不是match(),并去掉表达式中的.*前缀和后缀,应该会快得多。这些模式是硬编码的还是动态构建的?我可以建议使用展开模式,就像这个想法是好的一样,但是如果出现两次string2
,一次在string1
之前,一次在string1
之后,这将失败。最好先找到string1
,然后将其索引用作搜索string2
的开始索引。不幸的是,这不起作用,因为我的函数应该能够处理任何正则表达式。谢谢你的回答。@user100001太糟糕了,在一个有20mb文本的真实案例中,我尽可能多地使用了indexOf()
和contains()
,而regex只用于复杂的案例。在大型文档上节省使用regex的性能提高了几个数量级。完美。这比我的正则表达式表现得更好。谢谢你的回答。
public static boolean containsStrings(String source, String string1, String string2) {
long pos1, pos2;
pos1 = source.indexOf(string1);
if(pos1 > -1) {
pos2 = source.indexOf(string2,pos1 + string1.length);
if(pos2 > pos1 && source.indexOf(string1,pos2 + string2.length) < -1) {
return true;
}
}
return false;
}
int pos1 = str.indexOf("substring1");
int pos2 = str.indexOf("substring2", pos1);
if(pos1 != -1 && pos2 != -1){
// regex
}