【发布时间】:2010-09-06 23:45:40
【问题描述】:
我需要在 Perl 中使用正则表达式匹配和删除所有标签。我有以下内容:
<\\??(?!p).+?>
但这仍然与结束 </p> 标记匹配。关于如何与结束标签匹配的任何提示?
注意,这是在 xhtml 上执行的。
【问题讨论】:
-
请参阅此答案以了解您的 HTML/Regex 想法 - stackoverflow.com/questions/1732348/…
我需要在 Perl 中使用正则表达式匹配和删除所有标签。我有以下内容:
<\\??(?!p).+?>
但这仍然与结束 </p> 标记匹配。关于如何与结束标签匹配的任何提示?
注意,这是在 xhtml 上执行的。
【问题讨论】:
如果您坚持使用正则表达式,那么在大多数情况下,这样的事情会起作用:
# 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 模块的示例,该模块附带功能强大的 HTML::Parser 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::Parser 接受文件名、打开文件句柄或字符串形式的输入。将上面的代码包装在一个库中并使目标可配置(即,不仅仅是上面的printing)并不难。结果将比尝试使用正则表达式更加可靠、可维护,并且可能更快(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 解析器要容易得多。
【讨论】:
我想出了这个:
<(?!\/?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 标签,但将匹配带有或不带有属性的前标签和类似标签。
它没有去除属性,但我的源数据没有把它们放进去。我以后可能会改变它来做到这一点,但现在就足够了。
【讨论】:
不确定您为什么要这样做 - 用于 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
> # >
)
【讨论】:
我使用了 Xetius 正则表达式,它工作正常。除了一些 flex 生成的标签,它们可以是 :
里面没有空格。我尝试在 \s 之后用一个简单的 ? 修复它,看起来它正在工作:
<(?!\/?p(?=>|\s?.*>))\/?.*?>
我用它来清除 flex 生成的 html 文本中的标签,所以我还添加了更多例外标签:
<(?!\/?(p|a|b|i|u|br)(?=>|\s?.*>))\/?.*?>
【讨论】:
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
参考
【讨论】:
由于 HTML 不是正则语言,我不希望正则表达式在匹配它方面做得很好。他们可能能够胜任这项任务(尽管我不相信),但我会考虑寻找其他地方;我确信 perl 必须有一些现成的库来处理 HTML。
无论如何,我认为您想要匹配的是 ?(p.+|.*)(\s*.*)> 非贪婪(我不知道 perl 的正则表达式语法的变幻莫测,所以我无能为力)。我假设 \s 表示空格。也许不是。无论哪种方式,您都需要一些与标签名称偏移的属性相匹配的东西。但这比这更困难,因为人们经常将未转义的尖括号放在脚本和 cmets 中,甚至可能是引用的属性值,而您不想匹配这些值。
正如我所说,我真的不认为正则表达式是适合这项工作的工具。
【讨论】:
由于 HTML 不是常规语言
HTML 不是,但 HTML 标签是,它们可以用正则表达式充分描述。
【讨论】:
假设这将在 PERL 中像在声称使用 PERL 兼容语法的语言中一样工作:
/<\/?[^p][^>]*>/
编辑:
但是很遗憾,这与 <pre> 或 <param> 标记不匹配。
也许是这个?
/<\/?(?!p>|p )[^>]+>/
这应该包括<p> 也有属性的标签。
【讨论】:
您可能还希望在 p 标记中的“p”之前允许空格。不知道你多久会遇到这种情况,但 是完全有效的 HTML。
【讨论】:
原始的正则表达式可以轻松使用:
<(?>/?)(?!p).+?>
问题是 /? (或 \?)在断言失败后放弃匹配的内容。在它周围使用非回溯组 (?>...) 确保它永远不会释放匹配的斜杠,因此 (?!p) 断言始终锚定到标记文本的开头。
(也就是说,我同意通常用正则表达式解析 HTML 不是可行的方法)。
【讨论】:
试试这个,它应该可以工作:
/<\/?([^p](\s.+?)?|..+?)>/
说明:它匹配除“p”之外的单个字母,后跟可选的空格和更多字符,或者匹配多个字母(至少两个)。
/EDIT:我添加了处理p 标签中的属性的功能。
【讨论】:
这对我有用,因为上面的所有解决方案对于以 p 开头的其他 html 标签(例如 param pre progress 等)都失败了。它也处理了 html 属性。
~(<\/?[^>]*(?<!<\/p|p)>)~ig
【讨论】:
您可能还应该删除
标记上的所有属性,因为坏人可能会执行以下操作:
<p onclick="document.location.href='http://www.evil.com'">Clickable text</p>
执行此操作的最简单方法是使用人们在此处建议的正则表达式来搜索具有属性的 <p> 标记,并将其替换为不具有属性的
标记。只是为了安全起见。
【讨论】: