【问题标题】:Java regex performanceJava 正则表达式性能
【发布时间】:2010-10-11 22:53:51
【问题描述】:

我正在尝试使用 Java 解析带有正则表达式的链接。

但我认为它变得太慢了。例如,要从以下位置提取所有链接:

...它花费了 34642 毫秒(34 秒!!!)

这是正则表达式:

private final String regexp = "<a.*?\\shref\\s*=\\s*([\\\"\\']*)(.*?)([\\\"\\'\\s].*?>|>)";

模式的标志:

private static final int flags = Pattern.CASE_INSENSITIVE | Pattern.DOTALL |Pattern.MULTILINE | Pattern.UNICODE_CASE | Pattern.CANON_EQ;

而代码可能是这样的:

private void processURL(URL url){
    URLConnection connection;
    Pattern pattern = Pattern.compile(regexp, flags);
    try {
        connection = url.openConnection();
        InputStream in = connection.getInputStream();
        BufferedReader bf = new BufferedReader(new InputStreamReader(in));
        String html = new String();
        String line = bf.readLine();            
        while(line!=null){
            html += line;
            line = bf.readLine();
        }
        bf.close();
        Matcher matcher = pattern.matcher(html);
        while (matcher.find()) {
            System.out.println(matcher.group(2));
        }
     } catch (Exception e){
     }
 }

你能给我一个提示吗?

额外数据:
1Mbit
酷睿 2 双核
1Gb 内存
单线程

【问题讨论】:

  • 正则表达式在抓取网站!糟糕的糟糕的选择!
  • 如果您先下载整个页面,然后运行您的正则表达式,它会运行得更快吗?您确定是正则表达式花费了这么长时间而不是增量下载吗?
  • 是的 Steven,这是正则表达式的东西。我正在使用不同的解析器进行一些分析。
  • This answer 也回答了这个问题。

标签: java regex performance profiling benchmarking


【解决方案1】:

提示:不要将正则表达式用于链接提取或其他 HTML“解析”任务!

您的正则表达式中有 6 (六) 个重复组。执行它需要大量的回溯。在最坏的情况下,它甚至可能接近O(N^6),其中 N 是输入字符的数量。您可以通过用惰性匹配替换急切匹配来缓解这一点,但几乎不可能避免病态的情况;例如当输入数据的格式严重错误以至于正则表达式不匹配时。

一个更好的解决方案是使用一些现有的严格或宽松的 HTML 解析器。即使手动编写一个临时解析器也比使用粗糙的正则表达式要好。

This page 列出了用于 Java 的各种 HTML 解析器。我听说过关于 TagSoup 和 HtmlCleaner 的好消息。

【讨论】:

  • 谢谢兄弟,我试试javax.swing.text.html.HTMLEditorKit
【解决方案2】:

你所有的时间,所有都花在了这里:

 html+=line;

使用字符串缓冲区。更好的是,如果可以的话,在每一行上运行匹配并且根本不要累积它们。

【讨论】:

  • 好提示,无论如何,我需要构造所有的 HTML,因为行可能会被破坏。
  • 对不起 EJP。它不会根据使用正则表达式解析所需的大量时间来改变任何东西。它确实改变了一些东西,但与整体时间相比没有什么。不过这是一个很好的提示,我已经编写了代码。
  • 我知道这是一个老话题,但 user207421 说的是真的。我试过你的代码:我的机器上仍然需要 7120 毫秒。但是,当您将 String html 替换为 StringBuilder html 时,它会消耗 2200 毫秒。现在,大部分工作是检索数据。
【解决方案3】:

我编写了一个简单的测试,将 1000 万次操作 RegExp 性能与String.indexof() 进行比较,结果如下:

0.447 seconds
6.174 seconds
13.812080536912752 times regexp longer.

import java.util.regex.Pattern;

public class TestRegExpSpeed {
    public static void main(String[] args) {
        String match = "FeedUserMain_231_Holiday_Feed_MakePresent-1_";
        String unMatch = "FeedUserMain_231_Holiday_Feed_Make2Present-1_";

        long start = System.currentTimeMillis();
        for (int i = 0; i <= 10000000; i++) {
            if (i % 2 == 0) {
                match.indexOf("MakePresent");
            } else {
                unMatch.indexOf("MakePresent");
            }
        }

        double indexOf = (System.currentTimeMillis() - start) / 1000.;
        System.out.println(indexOf + " seconds");

        start = System.currentTimeMillis();
        Pattern compile = Pattern.compile(".*?MakePresent.*?");
        for (int i = 0; i <= 10000000; i++) {
            if (i % 2 == 0) {
                compile.matcher(match).matches();
            } else {
                compile.matcher(unMatch).matches();
            }
        }
        double reaexp = (System.currentTimeMillis() - start) / 1000.;
        System.out.println(reaexp + " seconds");

        System.out.println(reaexp / indexOf + " times regexp longer. ");
    }
}

【讨论】:

  • 这不是答案。是的,indexOf() 在特定任务中比正则表达式更快,但你为什么要使用正则表达式呢? OP 如何使用indexOf() 来解决他的问题?
【解决方案4】:

改用Jaunt。请不要为此使用正则表达式。

Regex use vs. Regex abuse

正则表达式不是解析器。 虽然你可以做一些惊人的事 带有正则表达式的东西,它们 在平衡标签匹配方面很弱。 一些正则表达式变体已经平衡 匹配,但它显然是一个黑客 - 和一个讨厌的。你经常可以做到 有点工作,就像我在 消毒常规。但无论如何 聪明你的正则表达式,不要欺骗 你自己:这绝不是,形状或 代替真实的生活 解析器。

Source

【讨论】:

  • 正则表达式是一项非常昂贵的操作。在抓取网站时,您需要解析大量文本。 acorns.com.au/blog/?p=136
  • 根据解析 HTML 页面的线性复杂度,计算正则表达式的复杂度是可怕的
猜你喜欢
  • 2018-11-09
  • 2013-11-18
  • 1970-01-01
  • 2011-11-22
  • 1970-01-01
  • 1970-01-01
  • 2015-12-29
  • 1970-01-01
  • 2011-05-31
相关资源
最近更新 更多