正则表达式匹配除 <p> 和 </p> 之外的所有 HTML 标记
题
我需要在 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>
>
</title>
</head>
<body>
<p>
>
</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";
看到这个 现场演示
参考
试试这个,它应该可以工作:
/<\/?([^p](\s.+?)?|..+?)>/
解释:它匹配除“p”之外的单个字母,后跟可选的空格和更多字符,或匹配多个字母(至少两个)。
/编辑:我添加了处理属性的能力 p
标签。
您可能还应该删除 <p> 标记上的任何属性,因为坏人可能会执行以下操作:
<p onclick="document.location.href='http://www.evil.com'">Clickable text</p>
最简单的方法是使用人们建议的正则表达式来搜索带有属性的 <p> 标签,并将其替换为不带属性的 <p> 标签。只是为了安全起见。