【问题标题】:Using Condition in Regular Expressions在正则表达式中使用条件
【发布时间】:2009-02-03 03:26:24
【问题描述】:

来源:

<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,the 无法识别“?(1)”。

书中的解释是:

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

【问题讨论】:

  • “10 分钟自学正则表达式”
  • 对于阅读此问题的任何人请注意,正则表达式的标准 java 包java.util.regex 不支持正则表达式中的条件。

标签: java regex exception


【解决方案1】:

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

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

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

【讨论】:

【解决方案2】:

正如 Bryan 所说,您正在查看的是一个条件构造,而 Java 不支持它们。问号后面的括号表达式实际上可以是任何零宽度断言,如前瞻或后瞻,而不仅仅是对捕获组的引用。 (为了避免混淆,我更喜欢称这些 back-assertions捕获组匹配某事。)

几年前我在 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));
}

【讨论】:

  • 关于反向引用和反向断言之间区别的好点。此外,+1 用于提供使用交替的解决方案。
【解决方案3】:

可能是这里描述的非捕获组:

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

Java Regex Tutorial

【讨论】:

    【解决方案4】:

    简短的回答:它没有任何意义。问题出在这整个sn-p上:

    (?(1)\s*)
    

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

    ? 表示它之前的项目如果存在则应该匹配,但如果它不存在也可以。当它出现在 (

    之后时,这根本没有意义

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

    试试:

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

    您从未准确说出您要匹配的内容,因此如果此答案不能满足您的要求,请说明您要使用 RegEx 做什么。

    【讨论】:

    • 正如 Bryan 所说,语法对于支持条件的正则表达式是正确的,但 Java 不支持。
    猜你喜欢
    • 2011-09-05
    • 2016-09-19
    • 1970-01-01
    • 2010-09-16
    • 2011-01-21
    • 1970-01-01
    • 2011-10-30
    • 2020-02-25
    • 2020-09-14
    相关资源
    最近更新 更多