模式优化

时间:2011-11-10 21:01:35

标签: java regex performance optimization screen-scraping

我需要使用Java从HTTP响应中删除一些内容。响应中的必填字段是:foo,bar和bla。我目前的模式很慢。任何想法如何改进?

响应:

...
<div class="ui-a">
<div class="ui-b">
    <p><strong>foo</strong></p>
    <p>bar</p>
</div>
<div class="ui-c">
    <p><strong>bla</strong></p>
    <p>...</p>
</div>
</div>

<div class="ui-a">
<div class="ui-b">
    <p><strong>foo1</strong></p>
    <p>bar1</p>
</div>
<div class="ui-c">
    <p><strong>bla1</strong></p>
    <p>...</p>
</div>

模式:

.*?<div class="ui-a">.*?<strong>(.*?)</strong>.*?<p>(.*?)</p>.*?</div>.*?<div class="ui-c">.*?<strong>(.*?)</strong>.*?

6 个答案:

答案 0 :(得分:2)

由于您无法使用HTML解析器,请尝试以下操作:

import java.util.regex.*;

public class Main {
    public static void main (String[] args) {
        String html =
                "...\n" +
                "<div class=\"ui-a\">\n" +
                "<div class=\"ui-b\">\n" +
                "    <p><strong>foo</strong></p>\n" +
                "    <p>bar</p>\n" +
                "</div>\n" +
                "<div class=\"ui-c\">\n" +
                "    <p><strong>bla</strong></p>\n" +
                "    <p>...</p>\n" +
                "</div>\n" +
                "</div>\n" +
                "\n" +
                "<div class=\"ui-a\">\n" +
                "<div class=\"ui-b\">\n" +
                "    <p><strong>foo1</strong></p>\n" +
                "    <p>bar1</p>\n" +
                "</div>\n" +
                "<div class=\"ui-c\">\n" +
                "    <p><strong>bla1</strong></p>\n" +
                "    <p>...</p>\n" +
                "</div>";

        Pattern p = Pattern.compile(
                "(?sx)                               # enable DOT-ALL and COMMENTS     \n" +
                "<div\\s+class=\"ui-a\">             # match '<div...ui-a...>'         \n" +
                "(?:(?!<strong>).)*+                 # match everything up to <strong> \n" +
                "<strong>([^<>]++)</strong>          # match <strong>...</strong>      \n" +
                "(?:(?!<p>).)*+                      # match up to <p>                 \n" +
                "<p>([^<>]++)</p>                    # match <p>...</p>                \n" +
                "(?:(?!<div\\s+class=\"ui-c\">).)*+  # match up to '<div...ui-a...>'   \n" +
                "<div\\s+class=\"ui-c\">             # match '<div...ui-c...>'         \n" +
                "(?:(?!<strong>).)*+                 # match everything up to <strong> \n" +
                "<strong>([^<>]++)</strong>          # match <strong>...</strong>      \n"
        );

        Matcher m = p.matcher(html);

        while(m.find()) {
            System.out.println("---------------");
            for(int i = 1; i <= m.groupCount(); i++) {
                System.out.printf("group(%d) = %s\n", i, m.group(i));
            }
        }
    }
}

将打印以下内容到控制台:

---------------
group(1) = foo
group(2) = bar
group(3) = bla
---------------
group(1) = foo1
group(2) = bar1
group(3) = bla1

请注意我的更改:

这应该更快(不确定多少......)。

答案 1 :(得分:1)

似乎,你要找的只是标签之间,你可以使用:

<strong>([a-zA-Z0-9]+)</strong>

此外,根据强标记内的内容,您可以更改模式,例如如果您确定文本总是小的情况下您可以从上面的模式中删除A-Z,或者如果它只包含4个字符,您可以在模式后使用{4}。

答案 2 :(得分:0)

将所有字符串转换为<p>标记,以便您可以搜索其中包含的内容(并删除<strong>)。 但如果你使用解析器而不是正则表达式可能会更好。 Search all <p>; If <p> has childNode then get <p>.text; else get <p>.text.

答案 3 :(得分:0)

请考虑使用JSoup。有些well-known problems使用正则表达式来解析HTML。

答案 4 :(得分:0)

如果您不依赖正则表达式来验证html,并且您没有权限修改html的结构。此外,摆脱最后的.*?是必要的,因为第一个将在后续比赛中发生冲突。基本上你有.*?.*?因为引擎会尝试查找最后<strong>标记和下一个<div class="ui-a">标记之间所有字符的所有可能排列。非常低效。试试这个:

.*?<div class="ui-a">.*?<strong>(.*?)</strong>.*?<p>(.*?)</p>.*?</div>.*?<div class="ui-c">.*?<strong>(.*?)</strong>

旁注:您确定要在<strong>内找到第一个<div class="ui-a">标记,因为第一个<strong>标记似乎发生在<div class="ui-b">内案例:

.*?<div class="ui-b">.*?<strong>(.*?)</strong>.*?<p>(.*?)</p>.*?</div>.*?<div class="ui-c">.*?<strong>(.*?)</strong>

更准确。

如果您知道所需的捕获组中没有嵌套标记,则可以使用以下方法进一步优化:

.*?<div class="ui-b">.*?<strong>([^<]*)</strong>.*?<p>([^<]*)</p>.*?</div>.*?<div class="ui-c">.*?<strong>([^<]*)</strong>

答案 5 :(得分:0)

你的正则表达式既有领导也有训练。*?我不明白为什么。如果数据格式良好,你只是意味着一定数量的空白,是吗?为什么不是:

Pattern p = "<div class=\"ui-b\">\s*<p><strong>([^<]*)</strong></p>\s*<p>([^<]*)</p>\s*</div>\s*<div class=\"ui-c\">\s*<p><strong>([^<]*)</strong></p>";
Matcher m = p.matcher(responseText);

while (m.find()) {
   String foo = m.group(1);
   String bar = m.group(2);
   String bla = m.group(3);

   /* do whatever w/ foo, bar, bla */
}

我放弃了所有.*?

并用空格替换内部的那些(或者在那里你可以省略更多 - 也许)。但无论如何,为什么你需要开始和结束。*?

如果格式良好,只需进行空格搜索就可以大幅增加。