是否有必要嵌套正则表达式?

时间:2010-12-07 01:10:03

标签: java regex

我想从HTML中提取出与此类似的两个数字10和11,只有它比我在这里显示的噪音更多:

<div a>
<noise=53>
<item=10>
<item=11>
</div>
<div b>
<item=20>
<noise=52>
<item=21>
</div>

我已经弄清楚如何使用两个正则表达式:首先使用

(?s)(?<=<div a>).*?(?=</div>)

获取“div a”部分中的内容,然后使用

(?s)(?<=<item=)[0-9]*

获得我想要的数字的结果。但我无法弄清楚如何只在一个正则表达式中做到这一点。我猜想如果只有Java让我把* s放在外观中,我会怎么做,但是Java没有(我模糊地理解为什么不这样做)。只有一个正则表达式可以做到这一点还是我应该选择两个?

6 个答案:

答案 0 :(得分:1)

我认为你不能归结为一个。但请注意,拆分HTML最好使用XML或HTML解析器。如果HTML是格式良好的XHTML,你可以使用XML解析器;否则请查看http://java-source.net/open-source/html-parsers

答案 1 :(得分:1)

我不完全确定嵌套正则表达式的意思。通常接近这种事情的方式是一次小心地拉出一点,就像一个词法分析器。这样你就不必尝试将所有东西都构建成一种模式。

您可以使用Matcher.matches()来查找当前起始点的内容,而不是使用Matcher.lookingat()。这样你就可以从同一个位置测试一堆它们。

类似的策略涉及使用Matcher.find()的单参数形式,其中您提供起始字符位置作为参数。

一个相关的功能是\G锚点,这是一个零宽度断言,使搜索在相同字符串的最后一个匹配处停止的位置启动。它可以节省一些簿记。

通过合理使用find(N)lookingat()方法(加上start()),或许可以使用\G断言,您可以为自己构建更灵活,更复杂的处理算法比仅使用单个正则表达式可行。

使用结构逻辑与常规Java管理你的正则表达式比试图在一个庞大的正则表达式中做所有事情要容易得多。以这种方式开发,调试和单元测试要容易得多。正则表达式最适合处理字符串,而不是尝试在其中编码整个解析算法。

另外,在Java中你不能真正做到这一点,因为在模式中不支持递归。也许它也一样,因为它鼓励你把控制结构放在外语中,因为你不能总是将你需要的所有内容都放在内部语言中。

答案 2 :(得分:1)

import java.util.regex.*;

public class Test
{
  public static void main(String[] args)
  {
    String s = "<div x><item=02><noise=99><item=05></div>\n" + 
        "<div a><noise=53><item=10><item=11><noise=55><item=12></div>\n" + 
        "<item=99>\n" + 
        "<div b><item=20><noise=52><item=21></div>";
    System.out.println(s);
    System.out.println();
    Pattern p = Pattern.compile(
        "(?:<div a>|\\G)(?:[^<]++|<(?!(?:item|/?div)\\b))*+<item=(\\d+)");
    Matcher m = p.matcher(s);
    while (m.find())
    {
      System.out.println(m.group(1));
    }
  }
}

输出:

<div x><item=02><noise=99><item=05></div>
<div a><noise=53><item=10><item=11><noise=55><item=12></div>
<item=99>
<div b><item=20><noise=52><item=21></div>

10
11
12

打破这种局面,我们有:

  • (?:<div a>|\\G)\G匹配上一场比赛停止的位置,如果之前没有匹配,则匹配文本的开头。在下一部分的前瞻中阻止了它的匹配,所以第一场比赛从<div a>开始。

  • (?:[^<]++|<(?!(?:item|/?div)\\b))*+:此部分消耗当前匹配位置与下一个<item=N>标记之间的任何位置。如果它不是<<<item序列的开头,它会吞噬除<div</div之外的所有字符。 (后两者确保所有<item=N>匹配包含在同一个div元素中;此外,<div是阻止\G在文本开头匹配的原因,并且</div会阻止 div元素之间的匹配,例如示例中的<item=99>

  • 最后,<item=(\\d+)item标记匹配,并捕获您所追求的数字。

答案 3 :(得分:0)

我认为 Sed 实用程序比使用正则表达式编程提取文本数据更有用。 请尝试Sed中的以下脚本(选项 -n )。

/<div \w>/,/<\/div>/ {
    s/.*item=\([0-9]\+\).*/\1/p
}

答案 4 :(得分:0)

如果它是真正的HTML,它可以转换为XML,例如通过HTMLTidy或NekoHTML,然后你应该使用XPath表达式。

答案 5 :(得分:0)

不要尝试,你需要一个解析器,很多都是可用的。