正则表达式匹配除<p>和</p>之外的所有HTML标记

时间:2008-08-27 10:41:45

标签: html regex perl

我需要在Perl中使用正则表达式匹配并删除所有标记。我有以下内容:

<\\??(?!p).+?>

但这仍然与结束</p>标记匹配。关于如何与结束标签匹配的任何提示?

请注意,这是在xhtml上执行的。

14 个答案:

答案 0 :(得分:37)

如果你坚持使用正则表达式,大多数情况下这样的东西都会起作用:

# Remove all HTML except "p" tags
$html =~ s{<(?>/?)(?:[^pP]|[pP][^\s>/])[^>]*>}{}g;

说明:

s{
  <             # opening angled bracket
  (?>/?)        # ratchet past optional / 
  (?:
    [^pP]       # non-p tag
    |           # ...or...
    [pP][^\s>/] # longer tag that begins with p (e.g., <pre>)
  )
  [^>]*         # everything until closing angled bracket
  >             # closing angled bracket
 }{}gx; # replace with nothing, globally

但实际上,请省去一些麻烦并使用解析器代替。 CPAN有几个适合的模块。以下是使用功能极为强大的HTML::TokeParser CPAN分发版附带的HTML::Parser模块的示例:

use strict;

use HTML::TokeParser;

my $parser = HTML::TokeParser->new('/some/file.html')
  or die "Could not open /some/file.html - $!";

while(my $t = $parser->get_token)
{
  # Skip start or end tags that are not "p" tags
  next  if(($t->[0] eq 'S' || $t->[0] eq 'E') && lc $t->[1] ne 'p');

  # Print everything else normally (see HTML::TokeParser docs for explanation)
  if($t->[0] eq 'T')
  {
    print $t->[1];
  }
  else
  {
    print $t->[-1];
  }
}

HTML::Parser接受文件名,打开文件句柄或字符串形式的输入。将上述代码包装在库中并使目的地可配置(即,不仅如上所述print)并不难。与尝试使用正则表达式相比,结果将更可靠,可维护,并且可能也更快(HTML :: Parser使用基于C的后端)。

答案 1 :(得分:16)

在我看来,尝试用HTML解析器以外的任何东西解析HTML只是在寻求一个痛苦的世界。 HTML是一种真正的复杂语言(这是创建XHTML的主要原因之一,它比HTML简单得多)。

例如,这个:

<HTML /
  <HEAD /
    <TITLE / > /
    <P / >

是一个完整的,100%格式良好,100%有效的HTML文档。 (好吧,它缺少DOCTYPE声明,但除此之外......)

它在语义上等同于

<html>
  <head>
    <title>
      &gt;
    </title>
  </head>
  <body>
    <p>
      &gt;
    </p>
  </body>
</html>

但它仍然是有效的HTML,你将不得不处理。当然,你可以设计一个正则表达式来解析它,但是,正如其他人已经建议的那样,使用实际的HTML解析器要简单得多。

答案 2 :(得分:10)

我想出了这个:

<(?!\/?p(?=>|\s.*>))\/?.*?>

x/
<           # Match open angle bracket
(?!         # Negative lookahead (Not matching and not consuming)
    \/?     # 0 or 1 /
    p           # p
    (?=     # Positive lookahead (Matching and not consuming)
    >       # > - No attributes
        |       # or
    \s      # whitespace
    .*      # anything up to 
    >       # close angle brackets - with attributes
    )           # close positive lookahead
)           # close negative lookahead
            # if we have got this far then we don't match
            # a p tag or closing p tag
            # with or without attributes
\/?         # optional close tag symbol (/)
.*?         # and anything up to
>           # first closing tag
/

现在,这将处理带有或不带属性的p标签和关闭p标签,但会匹配pre和类似标签,有或没有属性。

它不会删除属性,但我的源数据不会将它们放入。我可能会在稍后更改它,但现在就足够了。

答案 3 :(得分:3)

不确定为什么要这样做 - HTML清理的正则表达并不总是最好的方法(你需要记住清理属性等,删除javascript:hrefs等等)...但是,一个正则表达式匹配不是<p></p>的HTML标记:

(<[^pP].*?>|</[^pP]>)

详细:

(
    <               # < opening tag
        [^pP].*?    # p non-p character, then non-greedy anything
    >               # > closing tag
|                   #   ....or....
    </              # </
        [^pP]       # a non-p tag
    >               # >
)

答案 4 :(得分:3)

我使用Xetius正则表达式并且工作正常。除了一些flex生成的标签,它们可以是:
内部没有空格。我尝试在 \ s 之后用一个简单的修复它,看起来它正在工作:

<(?!\/?p(?=>|\s?.*>))\/?.*?>

我用它来清除flex生成的html文本中的标签,所以我还添加了更多的例外标签:

<(?!\/?(p|a|b|i|u|br)(?=>|\s?.*>))\/?.*?>

答案 5 :(得分:2)

由于HTML不是常规语言,我不希望正则表达式在匹配它时做得很好。他们可能完成这项任务(虽然我不相信),但我会考虑寻找其他地方;我确信perl必须有一些现成的库来操作HTML。

无论如何,我认为你想要匹配的是&lt; /?(p。+ |。*)(\ s *。*)&gt;非贪婪(我不知道perl的regexp语法的变幻莫测,所以我无法进一步帮助)。我假设\ s表示空格。也许它没有。无论哪种方式,您都希望通过空格匹配与标记名称相关的属性。但它比这更困难,因为人们经常将未转义的尖括号放在脚本和注释中,甚至可能引用属性值,这是你不想匹配的。

正如我所说,我并不认为正则表达式是适合这项工作的正确工具。

答案 6 :(得分:2)

  

由于HTML不是常规语言

HTML不是HTML标签,而且正则表达式可以充分描述它们。

答案 7 :(得分:1)

假设这可以在PERL中使用,就像声称使用PERL兼容语法的语言一样:

/<\/?[^p][^>]*>/

编辑:

但遗憾的是,这与<pre><param>标记不匹配。

这可能呢?

/<\/?(?!p>|p )[^>]+>/

这应该涵盖具有属性的<p>标记。

答案 8 :(得分:1)

您还可能希望在p标记中的“p”之前允许空格。不知道你会经常遇到这种情况,但是&lt; p为H.是完全有效的HTML。

答案 9 :(得分:1)

原始的正则表达式可以很轻松地工作:

 <(?>/?)(?!p).+?>

问题是/? (或\?)放弃了失败后的断言所匹配的内容。在它周围使用非回溯组(?> ...)时要注意它永远不会释放匹配的斜杠,因此(?!p)断言始终锚定在标记文本的开头。

(那表示我同意通常用正则表达式解析HTML不是一种方法)。

答案 10 :(得分:1)

Xetius,复活这个古老的问题,因为它有一个没有提到的简单解决方案。 (在为regex bounty quest进行一些研究时找到了您的问题。)

关于使用正则表达式解析html的所有免责声明,这是一种简单的方法。

#!/usr/bin/perl
$regex = '(<\/?p[^>]*>)|<[^>]*>';
$subject = 'Bad html <a> </I> <p>My paragraph</p> <i>Italics</i> <p class="blue">second</p>';
($replaced = $subject) =~ s/$regex/$1/eg;
print $replaced . "\n";

请参阅此live demo

参考

How to match pattern except in situations s1, s2, s3

How to match a pattern unless...

答案 11 :(得分:0)

试试这个,它应该有效:

/<\/?([^p](\s.+?)?|..+?)>/

说明:它匹配除“p”之外的单个字母,后跟可选的空格和更多字符,或多个字母(至少两个)。

/编辑:我添加了处理p代码中的属性的功能。

答案 12 :(得分:0)

这对我有用,因为对于其他以 p 开头的 html 标签(例如 param pre progress 等),上述所有解决方案都失败了。它也处理了 html 属性。

~(<\/?[^>]*(?<!<\/p|p)>)~ig

答案 13 :(得分:-1)

您可能还应删除&lt; p&gt;上的所有属性。标签,因为有人可能会做类似的事情:

<p onclick="document.location.href='http://www.evil.com'">Clickable text</p>

最简单的方法是使用人们在此处建议的正则表达式来搜索&amp; ltp&gt;带有属性的标记,并将其替换为&lt; p&gt;没有属性的标签。为了安全起见。