在正则表达式中使用条件

时间:2009-02-03 03:26:25

标签: java regex exception

来源:

<TD>
    <A HREF="/home"><IMG SRC="/images/home.gif"></A>
    <IMG SRC="/images/spacer.gif">
    <A HREF="/search"><IMG SRC="/images/search.gif"></A>
    <IMG SRC="/images/spacer.gif">
    <A HREF="/help"><IMG SRC="/images/help.gif"></A>
</TD>

正则表达式:

  (<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>(?(1)\s*</[Aa]>)

结果:

<A HREF="/home"><IMG SRC="/images/home.gif"></A>
<IMG SRC="/images/spacer.gif">
<A HREF="/search"><IMG SRC="/images/search.gif"></A>
<IMG SRC="/images/spacer.gif">
<A HREF="/help"><IMG SRC="/images/help.gif"></A>

什么是“?(1)”是什么意思?

当我在Java中运行它时,它会导致异常:java.util.regex.PatternSyntaxException, “?(1)”无法识别。

书中的解释是:

This pattern requires explanation. (<[Aa]\s+[^>]+>\s*)? matches an opening <A> or <a> tag (with any attributes that may be present), if present (the closing ? makes the expression optional). <[Ii][Mm][Gg]\s+[^>]+> then matches the <IMG> tag (regardless of case) with any of its attributes. (?(1)\s*</[Aa]>) starts off with a condition: ?(1) means execute only what comes next if backreference 1 (the opening <A> tag) exists (or in other words, execute only what comes next if the first <A> match was successful). If (1) exists, then \s*</[Aa]> matches any trailing whitespace followed by the closing </A> tag.

4 个答案:

答案 0 :(得分:3)

语法正确。奇怪的外观(?....)设置了条件。这是if ... then语句的正则表达式语法。 (1)是在正则表达式开始时对捕获组的反向引用,其匹配html&lt; a&gt;。 tag,如果有,则该捕获组是可选的。由于对捕获的标签的反向引用遵循正则表达式的“if”部分,因此它正在做的是确保存在开口&lt; a&gt;。在尝试匹配结束标记之前捕获的标记。一种非常聪明的方法,可以使两个标签都是可选的,但是当第一个标签存在时强制它们。这就是它能够匹配示例文本中所有行的方式,即使其中一些只是&lt; img&gt;标签

至于为什么它会在你的情况下抛出异常,很可能你正在使用的正则表达式的味道不支持条件。并非所有人都这样做。

编辑:这是关于正则表达式中条件的一个很好的参考:http://www.regular-expressions.info/conditional.html

答案 1 :(得分:3)

正如Bryan所说,你所看到的是一个条件结构,Java并不支持它们。紧跟在问号之后的带括号的表达式实际上可以是任何零宽度断言,如前瞻或后瞻,而不仅仅是对捕获组的引用。 (我更喜欢称那些 back-assertions ,以避免混淆。 back-reference 与捕获组所做的相同,但是一个后断言断言只是断言捕获组匹配某事。)

几年前我在Perl工作时学习了条件语,但我从未在Java中错过它们。在这种情况下,例如,一个简单的替换将成功:

(?i)<a\s+[^>]+>\s*<img\s+[^>]+>\s*</a]>|<img\s+[^>]+>

条件版本的一个优点是您可以使用单个捕获组捕获IMG标记:

(?i)(<a\s+[^>]+>\s*)?(<img\s+[^>]+>)(?(1)\s*</a>)

在交替版本中,你必须为每个替代版本都有一个捕获组,但这在Java中并不像在Perl中那么重要,它具有所有内置的正则表达式魔法。以下是我在Java中使用IMG标记的方法:

Pattern p = Pattern.compile(
  "<a\\s+[^>]+>\\s*(<img\\s+[^>]+>)\\s*</a>|(<img\\s+[^>]+>)"
  Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(s);
while (m.find())
{
  System.out.println(m.start(1) != -1 ? m.group(1) : m.group(2));
}

答案 2 :(得分:1)

这可能是非捕获组,如下所述:

  

还有一个特殊的小组,小组   0,总是代表整个   表达。该组不包括在内   在groupCount报告的总数中。   以(?开头的群组是纯粹的,   不捕获的非捕获组   捕获文本,不要指望   小组总数。 (你会看到例子   非捕获组的后期   section模式类的方法。)

Java Regex Tutorial

答案 3 :(得分:1)

简短的回答:这并不意味着什么。问题在于整个片段:

(?(1)\s*)

()会创建一个后引用,因此您可以重复使用内部匹配的任何文本。它们还允许您将运算符应用于它们内部的所有内容(但在您的示例中没有这样做)。

意味着它之前的项目应匹配,如果它在那里,但如果不是则也可以。当它出现在

之后)时,这根本没有意义

(?: MoreTextHere ) 当您不需要重用匹配的文本时,可用于加速RegEx。但这仍然没有意义,为什么当你的输入是HTML时匹配1?

尝试:

(?:<[Aa]\s+[^>]+>\s*)?<[Ii][Mm][Gg]\s+[^>]+>

你从未说出你想要匹配的内容,所以如果这个答案不能满足你的要求,请解释你在尝试用RegEx做什么。