我使用时收到StackOverflowError
遵循Reg Ex:
"([A-Z][A-Z]\\d\\d[A-Z]\\[(\\*|(((\\d|\\d\\d)-(\\d|\\d\\d))|(\\d|\\d\\d)))\\](,|$))+";
匹配类似String
的内容:
RA01D[1-1],RA01D[17-17],RA01D[2-2],RA01D[18-18]
答案 0 :(得分:4)
stribizhev's answer指出并修复的是正则表达式中的低效率。这里没有灾难性的回溯。此更改只会稍微延迟StackOverflowError
而不解决它(请参阅附录)。
在原始正则表达式中,如果第一个分支(\d|\d\d)-(\d|\d\d)
失败,第二个分支将再次匹配(\d|\d\d)
,这是第一个分支的前缀。
(
(
(\d|\d\d)-(\d|\d\d)
)
|
(\d|\d\d)
)
重写时(如他的回答所示),前缀(\d|\d\d)
只会匹配一次,引擎只需要检查2个不同的续集(匹配-(\d|\d\d)
或只是空字符串)。
(\d|\d\d)(?:-(\d|\d\d))?
这就是他的答案如何提高正则表达式的效率。同样的方法适用于\d|\d\d
。
回到StackOverflowError
的问题。如果在包含1000个元素的字符串上运行正则表达式,则上述任何正则表达式都将导致StackOverflowError
。这是由于Sun / Oracle / OpenJDK中的Pattern类的实现,它使用递归来进行贪婪和惰性量词。
由于正则表达式非模糊,您可以通过使最外层组的量词占有率来解决问题。正则表达式是从stribizhev的答案中复制而来的,但有一些修改:
"(?:[A-Z][A-Z]\\d\\d[A-Z]\\[(?:\\*|\\d{1,2}(?:-\\d{1,2})?)\\](?:,|$))++"
^^
由于实现使用循环来实现占有量词(因为不需要回溯),所以无论输入字符串有多长,都不会发生StackOverflowError
。堆栈使用量只有一次重复,与问题中的情况相反,它与字符串中元素的数量线性增长。
下面是一个测试程序,显示了正则表达式可以处理的元素数量。在我的系统(Oracle JRE,版本1.8.0_25)中,问题中的正则表达式只能在崩溃之前达到104 * 4 = 416个元素,stribizhev的答案设法达到137 * 4 = 548,stribizhev'修改了答案以删除不必要的组管理达到150 * 4 = 600,具有所有权量词的版本通过所有测试(500 * 4 = 2000元素)。
public class SO29758814 {
public static void main(String[] args) {
String s = "";
for (int i = 1; i <= 500; i++) {
s += "RA01D[1-1],RA01D[17-17],RA01D[2-2],RA01D[18-18],";
System.out.print(i);
try {
// Question
System.out.print(" 1 " + s.matches("([A-Z][A-Z]\\d\\d[A-Z]\\[(\\*|(((\\d|\\d\\d)-(\\d|\\d\\d))|(\\d|\\d\\d)))\\](,|$))+"));
} catch (Throwable e) { }
try {
// stribizhev's answer
System.out.print(" 2 " + s.matches("([A-Z]{2}\\d{2}[A-Z]\\[(\\*|((\\d{1,2})(?:-(\\d{1,2}))?))\\](?:,|$))+"));
} catch (Throwable e) { }
try {
// stribizhev's answer, remove unnecessary groups
System.out.print(" 3 " + s.matches("(?:[A-Z][A-Z]\\d\\d[A-Z]\\[(?:\\*|\\d{1,2}(?:-\\d{1,2})?)\\](?:,|$))+"));
} catch (Throwable e) { }
try {
// stribizhev's answer, remove unnecessary groups, use possessive quantifier
System.out.print(" 4 " + s.matches("(?:[A-Z][A-Z]\\d\\d[A-Z]\\[(?:\\*|\\d{1,2}(?:-\\d{1,2})?)\\](?:,|$))++"));
} catch (Throwable e) { }
System.out.println();
}
}
}
答案 1 :(得分:1)
Your regex包含具有类似模式的替代列表,这些模式通常会导致灾难性的回溯并可能影响性能。看看这种模式:
(
(
(\d|\d\d)-(\d|\d\d)
)
|
(\d|\d\d)
)
等于
(
(
(\d|\d\d)(?:-(\d|\d\d))?
)
)
此外,您最好使用量词,(\d|\d\d)
等于\d{1,2}
。
我还怀疑你需要捕获一个逗号或字符串结尾,所以添加一个非捕获组(?:,|$)
。
因此,请尝试使用此正则表达式(see demo here)
([A-Z]{2}\d{2}[A-Z]\[(\*|((\d{1,2})(?:-(\d{1,2}))?))\](?:,|$))+
或者作为Java字符串:
String pattern = "([A-Z]{2}\\d{2}[A-Z]\\[(\\*|((\\d{1,2})(?:-(\\d{1,2}))?))\\](?:,|$))+";
您还可以调整捕获组的数量。