10
4

4 回答 4

27

描述

小心正则表达式,<a[^>]*因为它们也会匹配其他以a诸如<abbr>或开头的有效 html 标记<address>。同样简单地寻找字符串的存在href是不够的,因为该字符串可能在另一个属性的值内,或者例如<a class="thishrefstuff"...,或者另一个属性的一部分,例如<a hreflang="en"...

该表达式将:

  • 匹配所有不包含属性的锚标签<a</a>href
  • 它将强制标签名称是a而不是简单地以字母开头的a标签<address>
  • 忽略在属性名称中也href嵌入了子字符串的属性,例如 validhreflang='en'或 made up Attributehref="some value"
  • 忽略所有格式正确的属性的值部分内的所有字符,例如bogus='href=""'

<a(?=\s|>)(?!(?:[^>=]|=(['"])(?:(?!\1).)*\1)*?\shref=['"])[^>]*>.*?<\/a>

在此处输入图像描述

展开

  • <a(?=\s|>)匹配打开标签并确保标签名称之后的下一个是空格或右括号,这会强制名称为a而不是其他
  • (?!如果我们在这个标签中找到一个href,那么这个类型的标签不是我们正在寻找的标签
    • (?:启动非捕获组以遍历标签内的所有字符
    • [^>=]匹配所有非标签结束字符,防止正则表达式引擎离开标签,非等号防止引擎继续盲目匹配所有字符
    • |或者
    • =(['"])匹配一个等号后跟一个开放的双引号或单引号。报价被捕获到第 2 组,因此以后可以正确配对
    • (?:(?!\1).)*匹配与开引号匹配的不是闭引号的所有字符
    • \1匹配正确的关闭引号
    • )*?关闭非捕获组并根据需要重复,直到
    • \shref=['"]匹配所需的 href 属性。and确保属性名称只是\shref=["']
    • )关闭负前瞻
  • [^>]*>.*?<\/a>匹配从打开到关闭的整个字符串

Java 代码示例:

输入文本

<abbr>RADIO</abbr> text <a class="aClass" href="#">link1</a> text <a bogus='href=""' class="aClass" target="_blank">link2</a> text

代码

如果您希望在替换函数中使用它来删除非 href-anchor 标记,那么只需将所有匹配项替换为空。

import java.util.regex.Pattern;
import java.util.regex.Matcher;
class Module1{
  public static void main(String[] asd){
  String sourcestring = "source string to match with pattern";
  Pattern re = Pattern.compile("<a(?=\\s|>)(?!(?:[^>=]|=(['\"])(?:(?!\\1).)*\\1)*?\\shref=['\"])[^>]*>.*?<\\/a>
",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
  Matcher m = re.matcher(sourcestring);
  int mIdx = 0;
    while (m.find()){
      for( int groupIdx = 0; groupIdx < m.groupCount()+1; groupIdx++ ){
        System.out.println( "[" + mIdx + "][" + groupIdx + "] = " + m.group(groupIdx));
      }
      mIdx++;
    }
  }
}

火柴

$matches Array:
(
    [0] => Array
        (
            [0] => <a bogus='href=""' class="aClass" target="_blank">link2</a>
        )

    [1] => Array
        (
            [0] => 
        )

)
于 2013-06-20T02:06:03.707 回答
6

我觉得你需要用正则表达式来做这件事很奇怪,但你可以使用负前瞻。

<a(?![^>]+href).*?>(.*?)</a>
于 2013-06-19T20:21:13.860 回答
0

我不是 Java 专家,但您可以尝试以下方法:

String regex = new String("(?i)<a(?>[^h>]++|(?<! )h++|h++(?!ref\\s*+=))*>((?>[^<]++|<(?!/a>))*)</a>");
String replacement = new String("$1");
str.replaceAll(regex,replacement);
于 2013-06-19T20:54:31.980 回答
-1

您拥有的一个选项是首先匹配所有 标签,然后使用正则表达式匹配具有的标签,以便您可以忽略它们。所以你的伪代码看起来像:

<a>tags = html.find(all<a>tags);
for(String <a>tag : <a>tags){
    if(<a>tag.isHref()) continue;
    //do proccessing
}
于 2013-06-19T20:23:11.630 回答