【问题标题】:RegEx to match <a> html tags without specific attribute正则表达式匹配没有特定属性的 <a> html 标记
【发布时间】:2013-06-16 12:48:01
【问题描述】:

在 Java 中,我需要在没有 href 属性的字符串中匹配 &lt;a&gt; 标记。例如在以下字符串中:

text <a class="aClass" href="#">link1</a> text <a class="aClass" target="_blank">link2</a> text

它不应该匹配&lt;a class="aClass" href="#"&gt;link1&lt;/a&gt;(因为它包含href)但它应该匹配&lt;a class="aClass" target="_blank"&gt;link2&lt;/a&gt;(因为它不包含href)。

我设法构建了正则表达式来匹配我的标签:

<a[^>]*>(.*?)</a>

但我不知道如何用 href 消除标签

(我知道我可以使用 HTML 解析器等,但我需要使用正则表达式。

【问题讨论】:

  • 为什么需要使用正则表达式? HTML is not a regular language.
  • 首选使用正则表达式解析 HTML 的原因有很多。一些编辑器将允许使用正则表达式进行搜索和替换,因为同一编辑器不支持“在此处插入您的 html 解析代码”。或者尝试从格式非常糟糕的 html 代码中提取数据可能会引发解析错误。或者家庭作业说使用正则表达式而不是解析引擎。或者如果文档甚至不是 html 而是包含 html 示例怎么办......同意或不同意,使用引擎解析 html 并不总是最好的解决方案。
  • 我很确定人们正在运行类似机器人的脚本,每当遇到“html”和“regex”字样的问题时,他们会自动发布指向该 的链接您无法使用正则表达式解析 [X]HTML..." 答案。搞笑。
  • 哈哈,他们可能也为此使用了解析引擎。

标签: java regex


【解决方案1】:

说明

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

这个表达式将:

  • 匹配所有不包含href属性的锚标记&lt;a...&lt;/a&gt;
  • 它将强制标记名称为a,而不是仅以字母a 开头的标记,例如&lt;address&gt;
  • 忽略在属性名称中也嵌入了子字符串href 的属性,例如有效的hreflang='en' 或组成的Attributehref="some value"
  • 忽略所有格式正确的属性的值部分内的所有字符,例如bogus='href=""'

&lt;a(?=\s|&gt;)(?!(?:[^&gt;=]|=(['"])(?:(?!\1).)*\1)*?\shref=['"])[^&gt;]*&gt;.*?&lt;\/a&gt;

展开

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

Java 代码示例:

输入文字

&lt;abbr&gt;RADIO&lt;/abbr&gt; text &lt;a class="aClass" href="#"&gt;link1&lt;/a&gt; text &lt;a bogus='href=""' class="aClass" target="_blank"&gt;link2&lt;/a&gt; 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] => 
        )

)

【讨论】:

  • 完美答案。它适用于我正在尝试做的事情。唯一的问题(对于其他人)可能是如果没有引号的 href 属性,它将匹配它。例如:&lt;a href=http://www.java.com&gt;&lt;/a&gt;
【解决方案2】:

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

String regex = new String("(?i)<a(?>[^h>]++|(?<! )h++|h++(?!ref\\s*+=))*>((?>[^<]++|<(?!/a>))*)</a>");
String replacement = new String("$1");
str.replaceAll(regex,replacement);

【讨论】:

    【解决方案3】:

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

    <a>tags = html.find(all<a>tags);
    for(String <a>tag : <a>tags){
        if(<a>tag.isHref()) continue;
        //do proccessing
    }
    

    【讨论】:

    • -1 OP 指定了如何使用正则表达式而不是 html 解析引擎来执行此操作。
    • @Denomales 它的伪代码...不是 html 解析引擎。我不会在任何地方链接或说它是解析器,我只是建议使用正则表达式的另一种用法。
    【解决方案4】:

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

    <a(?![^>]+href).*?>(.*?)</a>
    

    【讨论】:

    • @Raedwald 可以使用 DOTALL
    猜你喜欢
    • 2012-02-18
    • 2019-10-09
    • 2014-08-30
    • 2019-04-12
    • 2015-03-15
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多