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

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

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

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

有帮助吗?

解决方案 3

我想出了这个:

<(?!\/?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 和类似标签。

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

其他提示

如果你 坚持 使用正则表达式,在大多数情况下,类似这样的事情会起作用:

# 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::Toke解析器 具有极其强大功能的模块 HTML::解析器 CPAN分布:

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

在我看来,尝试使用 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 解析器要容易得多。

不知道为什么你想要这样做 - 用于 HTML 清理的正则表达式并不总是最好的方法(你需要记住清理属性等,删除 javascript:href 等)...但是,一个正则表达式可以匹配不存在的 HTML 标签 <p></p>:

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

详细:

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

我使用了 Xetius 正则表达式,效果很好。除了一些 Flex 生成的标签之外,这些标签可以是:
里面没有空格。我尝试用简单的方法修复它 ?\s 看起来它正在工作:

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

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

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

由于 HTML 不是一种正则语言,我不希望正则表达式能够很好地匹配它。他们可能能够胜任这项任务(尽管我不相信),但我会考虑寻找其他地方;我确信 Perl 一定有一些现成的库来操作 HTML。

无论如何,我认为你想要匹配的是 </?(p.+|.*)(\s*.*)> 非贪婪的(我不知道 perl 正则表达式语法的变幻莫测,所以我无能为力更远)。我假设 \s 表示空格。也许事实并非如此。无论哪种方式,您都需要能够匹配通过空格从标签名称偏移的属性。但这比这更困难,因为人们经常将未转义的尖括号放在脚本和注释中,甚至可能引用您不希望与之匹配的属性值。

正如我所说,我真的不认为正则表达式是完成这项工作的正确工具。

由于 HTML 不是常规语言

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

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

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

编辑:

但这不会匹配 <pre> 或者 <param> 标签,不幸的是。

也许是这个?

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

这应该涵盖 <p> 标签也有属性。

您可能还希望在 p 标记中的“p”之前允许有空格。不确定您多久会遇到这种情况,但 < p> 是完全有效的 HTML。

原始的正则表达式可以毫不费力地工作:

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

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

(也就是说,我同意一般来说用正则表达式解析 HTML 不是正确的方法)。

Xetius,复活了这个古老的问题,因为它有一个没有提到的简单解决方案。(在做一些研究时发现你的问题 正则表达式赏金任务.)

对于使用正则表达式解析 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";

看到这个 现场演示

参考

如何匹配除 s1、s2、s3 情况之外的模式

如何匹配模式,除非......

试试这个,它应该可以工作:

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

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

/编辑:我添加了处理属性的能力 p 标签。

您可能还应该删除 <p> 标记上的任何属性,因为坏人可能会执行以下操作:

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

最简单的方法是使用人们建议的正则表达式来搜索带有属性的 &ltp> 标签,并将其替换为不带属性的 <p> 标签。只是为了安全起见。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top