用于查找 HTML 标签及其内容的正则表达式的否定 - java

Posted

技术标签:

【中文标题】用于查找 HTML 标签及其内容的正则表达式的否定 - java【英文标题】:Negation of regex for finding HTML tags and their content - java 【发布时间】:2019-10-02 21:40:39 【问题描述】:

我正在 uni 做一个项目,我必须使用正则表达式清理一些 html 代码(我知道,这不是最好的方法......)

正文输入:

<h1>This is heading 1</h1>
<h2 style="color: aqua">This is heading 2</h2>
<h3>This is heading 3</h3>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<a href="https://www.w3schools.com">This is a link</a>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

我得到了一个允许的标签列表,我还必须删除所有其他标签及其内容。例如h3, p, ul

首先我删除所有参数(不允许使用它们),然后我想出了这个正则表达式,它删除了标签和内容。

String regex = "(?i)<([h3|ul|p]+)>\\n?.*\\n?<\\/\\1>";

它可以工作,但现在我必须否定它并删除所有标签和内容,除了...中给出的那些标签和内容......

我试过了,但没有用:

`...[?!h3|ul|p]...`

此示例的预期结果:

<h3>This is heading 3</h3>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<ul>
</ul>

不太了解 Negative Lookahead 以及如何将其应用于我的问题,因此我将不胜感激。

【问题讨论】:

"我试过了,但是不行:...[?!h3|ul|p]..." 否定匹配的语法是@ 987654328@ 被称为“零宽度负前瞻”。有关显示语法的 javadoc,请参阅 here,有关说明,请参阅 here。 仅供参考: 在 Java 中,您不需要转义 /,因为这不是特殊字符。它只是用/“引用”正则表达式的语言中的一个特殊字符,即正则表达式写为/xxx/ 【参考方案1】:

您尝试使用的负面展望需要写成(?!(?:h3|ul|p)\b),它不会选择h3ulp 标记。注意在它之后使用单词边界\b 以拒绝这些标签的完全匹配。除了删除这些标签之外,您还必须删除删除这些标签后留下的空格,因此总体而言,您需要使用的正则表达式是这样的,

\h*<(?!(?:h3|ul|p)\b)([^>]+).*?>[\w\W]*?</\1>\s*

正则表达式解释:

\h* - 匹配标记前的零个或多个水平空格(空格和制表符,可能是其他存在的) &lt; - 标签开始 (?!(?:h3|ul|p)\b) - 负前瞻以完全拒绝 h3 ulp 标签 ([^&gt;]+) - 匹配标签名称一个或多个字符,并在 group1 中捕获以供以后反向引用。您可以使用 \w+ 之类的内容或包含允许字符的字符集来仅匹配您想要的内容。 .*?&gt; - 可以选择匹配零个或多个字符(基本上是属性),然后用 &gt; 关闭开始标签 [\w\W]*? - 以非贪婪方式匹配任何零个或多个字符,包括换行符 &lt;/\1&gt; - 匹配标签的结束,\1 表示之前匹配的标签名称 \s* - 匹配零个或多个空格,这基本上消耗了删除标签所产生的空白空间

Regex Demo

Java 代码演示,

String s = "<h1>This is heading 1</h1>\r\n" + 
        "<h2 style=\"color: aqua\">This is heading 2</h2>\r\n" + 
        "<h3>This is heading 3</h3>\r\n" + 
        "<p>This is a paragraph.</p>\r\n" + 
        "<p>This is another paragraph.</p>\r\n" + 
        "<a href=\"https://www.w3schools.com\">This is a link</a>\r\n" + 
        "<ul>\r\n" + 
        "  <li>Coffee</li>\r\n" + 
        "  <li>Tea</li>\r\n" + 
        "  <li>Milk</li>\r\n" + 
        "</ul>";

System.out.println("Before:\n" + s);
System.out.println("\nAfter:\n" + s.replaceAll("\\h*<(?!(?:h3|ul|p)\\b)([^>]+).*?>[\\w\\W]*?</\\1>\\s*", ""));

输出,

Before:
<h1>This is heading 1</h1>
<h2 style="color: aqua">This is heading 2</h2>
<h3>This is heading 3</h3>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<a href="https://www.w3schools.com">This is a link</a>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

After:
<h3>This is heading 3</h3>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<ul>
</ul>

【讨论】:

【参考方案2】:

您可能希望提取那些您希望在所需输出中出现的内容。此表达式可能是更好的选择,如果您愿意,可以对其进行修改:

(<(p|h3.*)>.*<\/(.*)>)|(<(ul.*)>[\s\S]*<\/(ul)>)

它有两个组,一个用于 p 和 h3,另一个用于 ul,您可以将它们包装到另一个捕获组:

((<(p|h3.*)>.*<\/(.*)>)|(<(ul.*)>[\s\S]*<\/(ul)>))

正则表达式

如果这不是您想要的表达式,您可以在regex101.com 中修改/更改您的表达式。

正则表达式电路

您还可以在jex.im 中可视化您的表达式:

Java 测试

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "((<(p|h3.*)>.*<\\/(.*)>)|(<(ul.*)>[\\s\\S]*<\\/(ul)>))";
final String string = "<h1>This is heading 1</h1>\n"
     + "<h2 style=\"color: aqua\">This is heading 2</h2>\n"
     + "<h3>This is heading 3</h3>\n"
     + "<p>This is a paragraph.</p>\n"
     + "<p>This is another paragraph.</p>\n"
     + "<a href=\"https://www.w3schools.com\">This is a link</a>\n"
     + "<ul>\n"
     + "  <li>Coffee</li>\n"
     + "  <li>Tea</li>\n"
     + "  <li>Milk</li>\n"
     + "</ul>";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);

while (matcher.find()) 
    System.out.println("Full match: " + matcher.group(0));
    for (int i = 1; i <= matcher.groupCount(); i++) 
        System.out.println("Group " + i + ": " + matcher.group(i));
    

javascript 演示

const regex = /((<(p|h3.*)>.*<\/(.*)>)|(<(ul.*)>[\s\S]*<\/(ul)>))/gm;
const str = `<h1>This is heading 1</h1>
<h2 style="color: aqua">This is heading 2</h2>
<h3>This is heading 3</h3>
<p>This is a paragraph.</p>
<p>This is another paragraph.</p>
<a href="https://www.w3schools.com">This is a link</a>
<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>`;
let m;

while ((m = regex.exec(str)) !== null) 
    // This is necessary to avoid infinite loops with zero-width matches
    if (m.index === regex.lastIndex) 
        regex.lastIndex++;
    
    
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => 
        console.log(`Found match, group $groupIndex: $match`);
    );

此表达式可能只捕获您想要的输出。它不遵循否定策略。

【讨论】:

这不会删除允许标签内的不允许标签,除非您递归地应用正则表达式。它也不会在任何标签之外保留测试。例如。由于不允许使用 &lt;li&gt; 标记,因此应将其删除,如问题中的“期望结果”所示,但您的解决方案会保留它们。 @Emma 是的,这是一个相当大的问题,因为当我使用您的方法时,我只需要提取允许的标签,而不是其中的标签(可能不允许)......知道如何然后继续?非常感谢您的评论,我已经在尝试了。

以上是关于用于查找 HTML 标签及其内容的正则表达式的否定 - java的主要内容,如果未能解决你的问题,请参考以下文章

用于查找html标签的正则表达式[重复]

特定标签及其内容的正则表达式,按标签名称分组

请问在notepad++中,如何选中匹配标签及其之间的内容?

用于否定字符类的 C# 正则表达式,除非字符彼此相邻

用于删除 XML 标记及其内容的正则表达式

用于匹配任意两个 HTML 标签的正则表达式