当匹配包含\ n的行时,Java Pattern.matcher()会冻结

时间:2013-05-23 15:21:36

标签: java regex

我遇到了一个我觉得非常有趣的问题。我正在使用正则表达式对文本文件进行一些基本的解析,并且在匹配此行时它总是会冻结

ftrect 0.7031 57.0313 9.8561 55.5313 "FREIGABE \nQ09_SV01"

没有抛出异常;该程序只是挂起。我正在发布重新创建情况的程序片段;评论的是可能的标准情况,但另一个是有问题的。如果你删除\ n它有效,但这些解析过的文件来自“blackbox”系统。

我当然可以做一个解决方法,我发现它实际上已经冻结并希望有人可以解释正在发生的事情。我在JDK6u22和JDK7u21上试过......

public static Pattern FTRECT_PATTERN = Pattern.compile(
    "\\s*([\\w]+)?\\:?\\s*ftrect\\s+((\\d*\\.?\\d*\\s?)+)\\s*\"?([\\w\\s\\.\\%\\/\\=]*)?\"?\\s*"
);

public static void main(String[] args) {

//  Matcher m = FTRECT_PATTERN.matcher( "FOX_BACKGROUND: ftrect 46.1719 18.0556 54.8633 16.5556 \"Schicht\" " );
    Matcher m = FTRECT_PATTERN.matcher( "ftrect 0.7031 57.0313 9.8561 55.5313 \"FREIGABE \\nQ09_SV01\"" );
    System.out.println( m.matches() );

    for (int i = 0; i <= m.groupCount(); i++) {
        String string = m.group( i );
        System.out.println( string );
    }
}

好吧,我发现如果我将正则表达式修改为此(将\\\\添加到最后一个组):

public static Pattern FTRECT_PATTERN = Pattern.compile(
    "\\s*([\\w]+)?\\:?\\s*ftrect\\s+((\\d*\\.?\\d*\\s?)+)\\s*\"?([\\w\\\\\\s\\.\\%\\/\\=]*)?\"?\\s*"
);

我仍然不知道为什么没有抛出异常。

1 个答案:

答案 0 :(得分:9)

这是因为catastrophic backtracking。您的测试字符串包含一个与字符类"...\\n..."不匹配的文字反斜杠(在[\w\s\.\%\/\=]*中)。

这意味着正则表达式引擎必须尝试在违规字符之前的字符串"FREIGABE的所有可能排列,然后才能决定不匹配。

这是一个非常高的数字,可能会让引擎忙碌几个小时。将反斜杠添加到字符类后,正则表达式可以匹配。

预防:使用占有量词(*+++)以避免无用的回溯:

public static Pattern FTRECT_PATTERN = Pattern.compile( "\\s*([\\w]+)?\\:?\\s*ftrect\\s+((\\d*\\.?\\d*\\s?)++)\\s*\"?([\\w\\s\\.\\%\\/\\=]*+)?\"?\\s*" );

更好的清理解决方案是:

public static Pattern FTRECT_PATTERN = Pattern.compile("\\s*(\\w*):?\\s*ftrect\\s+((\\b\\d*(?:\\.\\d+)?\\b\\s?)+)\\s*\"?([\\\\\\w\\s.%/=]*+)?\"?\\s*");