【问题标题】:Java Regular Expression running very slowJava 正则表达式运行很慢
【发布时间】:2011-02-16 01:42:41
【问题描述】:

我正在尝试在 Java 中使用 Daring Fireball Regular Expression for matching URLs,但我发现了一个导致评估持续进行的 URL。我已经修改了原始的正则表达式以使用 Java 语法。

private final static String pattern = 
"\\b" + 
"(" +                            // Capture 1: entire matched URL
  "(?:" +
    "[a-z][\\w-]+:" +                // URL protocol and colon
    "(?:" +
      "/{1,3}" +                        // 1-3 slashes
      "|" +                             //   or
      "[a-z0-9%]" +                     // Single letter or digit or '%'
                                        // (Trying not to match e.g. "URI::Escape")
    ")" +
    "|" +                            //   or
    "www\\d{0,3}[.]" +               // "www.", "www1.", "www2." … "www999."
    "|" +                            //   or
    "[a-z0-9.\\-]+[.][a-z]{2,4}/" +  // looks like domain name followed by a slash
  ")" +
  "(?:" +                           // One or more:
    "[^\\s()<>]+" +                      // Run of non-space, non-()<>
    "|" +                               //   or
    "\\((?:[^\\s()<>]+|(?:\\([^\\s()<>]+\\)))*\\)" +  // balanced parens, up to 2 levels
  ")+" +
  "(?:" +                           // End with:
    "\\((?:[^\\s()<>]+|(?:\\([^\\s()<>]+\\)))*\\)" +  // balanced parens, up to 2 levels
    "|" +                                   //   or
    "[^\\s`!\\-()\\[\\]{};:'\".,<>?«»“”‘’]" +        // not a space or one of these punct chars (updated to add a 'dash'
  ")" +
")";

// @see http://daringfireball.net/2010/07/improved_regex_for_matching_urls
private static final Pattern DARING_FIREBALL_PATTERN = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);

如果我尝试运行以下命令,则需要很长时间。我已将其范围缩小到平衡括号的匹配(我认为)。如果您更改括号内的文本,它可以正常工作,但在大约 15 个字符时,它开始呈指数级下降。

final Matcher matcher = pattern.matcher("https://goo.gl/a(something_really_long_in_balanced_parens)");
boolean found = matcher.find();

有没有办法改进这个正则表达式,使关于的行不会永远占用?我在一个 JUnit 测试类中有大约 100 个不同的 URL,我需要这些 URL 来继续工作。

【问题讨论】:

    标签: java regex performance


    【解决方案1】:

    问题出在这里:

    "(?:" +                           // One or more:
    "[^\\s()<>]+" +                      // Run of non-space, non-()<>
    "|" +                               //   or
    "\\((?:[^\\s()<>]+|(?:\\([^\\s()<>]+\\)))*\\)" +  // balanced parens, up to 2 levels
    ")+"
    

    你在这里得到的是嵌套量词。这会对任何回溯算法造成严重破坏 - 例如,考虑匹配字符串的正则表达式 /^(a+)+$/

    aaaaaaaaaab
    

    作为第一次尝试,内部量词将匹配所有as。然后正则表达式失败,所以它回退一个。然后外部量词再次尝试匹配,吞噬最后一个a,然后正则表达式再次失败。我们基本上得到指数行为,因为量词尝试了各种方法来拆分 as 的运行,但实际上没有任何进展。

    解决方案是占有量词(我们通过将+附加到量词的末尾来表示) - 我们设置了内部量词,以便一旦它们匹配,它们就不会'不要放手——他们会一直坚持到匹配失败或较早的量词退出并且他们必须从字符串中的其他位置开始重新匹配。如果我们改为使用 /^(a++)+$/ 作为我们的正则表达式,我们将在上面的不匹配字符串上立即失败,而不是指数化地尝试匹配它。

    尝试使这些内部量词具有所有格,看看是否有帮助。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 2021-11-25
    相关资源
    最近更新 更多