Perl:从字符串中删除剩余的锚标记
Perl: Removing leftover anchor tags from a string
我编写了一个脚本来检查 HTML 文件中的所有 URL,并使用 HTTP::Tiny 删除它们并删除,但它在整个过程中留下了一堆不完整的锚标记,如 <a>text here</a>
数据库。这些更改已经完成,脚本不需要再次 运行。
脚本从存储此信息的数据库中读取。
现在的目标是去除 <a>text here</a>
,同时将缓冲区中的所有其他内容以及“此处的文本”文本保留下来。
我编写了另一个读取数据库并包含以下正则表达式的脚本:
$html_buffer =~ s/(.*?)<a>(.*?)<\/a>(.*?)//g;
但它不起作用,我不知道为什么。这是一个示例缓冲区:
This week, perhaps the most interesting articles include "<a>Finding \r\n that Windows is superior to Linux is biased</a>," "<a href=\"http://www.example.com/content/view/118693\">How \r\n to set up DNS for Linux VPNs</a>," and "<a href=\"http://www.example.com/content/view/118664
\">Writing \r\n an Incident Handling and Recovery Plan</a>."
当然我希望正则表达式在整个字符串中运行,但也匹配字符串中的多次出现。
这是最有效的方法吗?我怎样才能确保不像上面的例子那样删除字符串末尾的 </a>
?
原则上,始终建议使用合适的解析器。第二部分有一个例子
正则表达式需要 /s
修饰符,以便 .
也匹配换行符,然后它就可以工作了。没有它,模式 .*?
会在换行处停止。只是碰巧正则表达式仍然设法匹配一些字符串,看起来“有效但不正确”。
要点是,如果该字符串被分配为 double-quoted
,则其中包含 \n
s 的字符串具有换行符
my $html_buffer = " ... "; # or: = qq(...);
因为 \n
被解释为换行符。如果该字符串用单引号给出,
my $html_buffer = ' ... '; # or: = q(...);
然后里面就没有换行符了,只是偶尔出现的字符 \
和 n
一个接一个,而 .*?
按预期工作。
最后:替换运算符仅影响已匹配的字符串部分(或更少,如果模式被写成“删除”一些匹配项,例如 \K
),所以没有理由前导和尾随 (.*?)
。只需要
$html_buffer =~ s{<a>(.*?)</a>}{}sg;
为了安全起见,在所有这些模式中添加一点额外的空格,<\s*a\s*>
所以
$html_buffer =~ s{<\s*a\s*>(.*?)<\s*/a\s*>}{}sg;
请注意,我发现修复原始脚本的解决方案要好得多,这样它就不会留下意外的点点滴滴。我敢猜测,这是通过对 HTML 使用正则表达式发生的吗?令人惊讶的是,任何为解析 HTML/XML 而编写的主要库都会像那样削弱(我认为有效)HTML。
另一方面,现在也可以做到这一点。如果剩余的文本及其 not-links(没有 href
属性的 <a>
在技术上不是超链接)非常简单,那么使用正则表达式(手指交叉)可能会更简单;就这一次。
在所有其他情况下,这里有一个非常基本的 Mojo::DOM
use warnings;
use strict;
use feature 'say';
use Mojo::DOM;
my $html = q(<p> a link: <a>no href</a>, not. <p> OK: <a href="#">hoho</a>);
my $dom = Mojo::DOM->new( $html );
say $dom;
foreach my $link ($dom->find("a")->each) {
if (not defined $link->attr->{href}) {
# Replace this node with its text
$link->replace( $link->text );
}
}
say $dom; # object stringifies to just its HTML
(或 $link->strip;
删除 HTML 元素但保留其内容,结果相同)
这会打印
<p> a link: <a>no href</a>, not. </p><p> OK: <a href="#">hoho</a></p>
<p> a link: no href, not. </p><p> OK: <a href="#">hoho</a></p>
我使用了较短的 made-up HTML 字符串,但我也检查了问题的示例。 Mojo
还有其他方法可以做到这一点,探索它们很有趣(也很有用)。
例如,仅处理未定义 href
属性的链接,方法是先过滤
$_->replace($_->text) for
$dom -> find("a")
-> grep( sub { not defined $_->attr->{href} } )
-> each;
或者,直接处理
$dom -> find("a")
-> each( sub {
$_->replace($_->text) if not defined $_->attr->{href}
});
我编写了一个脚本来检查 HTML 文件中的所有 URL,并使用 HTTP::Tiny 删除它们并删除,但它在整个过程中留下了一堆不完整的锚标记,如 <a>text here</a>
数据库。这些更改已经完成,脚本不需要再次 运行。
脚本从存储此信息的数据库中读取。
现在的目标是去除 <a>text here</a>
,同时将缓冲区中的所有其他内容以及“此处的文本”文本保留下来。
我编写了另一个读取数据库并包含以下正则表达式的脚本:
$html_buffer =~ s/(.*?)<a>(.*?)<\/a>(.*?)//g;
但它不起作用,我不知道为什么。这是一个示例缓冲区:
This week, perhaps the most interesting articles include "<a>Finding \r\n that Windows is superior to Linux is biased</a>," "<a href=\"http://www.example.com/content/view/118693\">How \r\n to set up DNS for Linux VPNs</a>," and "<a href=\"http://www.example.com/content/view/118664
\">Writing \r\n an Incident Handling and Recovery Plan</a>."
当然我希望正则表达式在整个字符串中运行,但也匹配字符串中的多次出现。
这是最有效的方法吗?我怎样才能确保不像上面的例子那样删除字符串末尾的 </a>
?
原则上,始终建议使用合适的解析器。第二部分有一个例子
正则表达式需要 /s
修饰符,以便 .
也匹配换行符,然后它就可以工作了。没有它,模式 .*?
会在换行处停止。只是碰巧正则表达式仍然设法匹配一些字符串,看起来“有效但不正确”。
要点是,如果该字符串被分配为 double-quoted
,则其中包含\n
s 的字符串具有换行符
my $html_buffer = " ... "; # or: = qq(...);
因为 \n
被解释为换行符。如果该字符串用单引号给出,
my $html_buffer = ' ... '; # or: = q(...);
然后里面就没有换行符了,只是偶尔出现的字符 \
和 n
一个接一个,而 .*?
按预期工作。
最后:替换运算符仅影响已匹配的字符串部分(或更少,如果模式被写成“删除”一些匹配项,例如 \K
),所以没有理由前导和尾随 (.*?)
。只需要
$html_buffer =~ s{<a>(.*?)</a>}{}sg;
为了安全起见,在所有这些模式中添加一点额外的空格,<\s*a\s*>
所以
$html_buffer =~ s{<\s*a\s*>(.*?)<\s*/a\s*>}{}sg;
请注意,我发现修复原始脚本的解决方案要好得多,这样它就不会留下意外的点点滴滴。我敢猜测,这是通过对 HTML 使用正则表达式发生的吗?令人惊讶的是,任何为解析 HTML/XML 而编写的主要库都会像那样削弱(我认为有效)HTML。
另一方面,现在也可以做到这一点。如果剩余的文本及其 not-links(没有 href
属性的 <a>
在技术上不是超链接)非常简单,那么使用正则表达式(手指交叉)可能会更简单;就这一次。
在所有其他情况下,这里有一个非常基本的 Mojo::DOM
use warnings;
use strict;
use feature 'say';
use Mojo::DOM;
my $html = q(<p> a link: <a>no href</a>, not. <p> OK: <a href="#">hoho</a>);
my $dom = Mojo::DOM->new( $html );
say $dom;
foreach my $link ($dom->find("a")->each) {
if (not defined $link->attr->{href}) {
# Replace this node with its text
$link->replace( $link->text );
}
}
say $dom; # object stringifies to just its HTML
(或 $link->strip;
删除 HTML 元素但保留其内容,结果相同)
这会打印
<p> a link: <a>no href</a>, not. </p><p> OK: <a href="#">hoho</a></p>
<p> a link: no href, not. </p><p> OK: <a href="#">hoho</a></p>
我使用了较短的 made-up HTML 字符串,但我也检查了问题的示例。 Mojo
还有其他方法可以做到这一点,探索它们很有趣(也很有用)。
例如,仅处理未定义 href
属性的链接,方法是先过滤
$_->replace($_->text) for
$dom -> find("a")
-> grep( sub { not defined $_->attr->{href} } )
-> each;
或者,直接处理
$dom -> find("a")
-> each( sub {
$_->replace($_->text) if not defined $_->attr->{href}
});