my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>';
有开始和结束标记<a>
和</a>
,有ABC,DEF和GHI键,但被其他一些随机文本包围。我想将<a>klashsdjDEFasl;jjf</a>
替换为<b>TEST</b>
例如。但是,如果我有这样的事情:
$str =~ s/<a>.*?DEF.*?<\/a>/<b>TEST><\/b>/;
即使使用非贪婪的运算符。*?,这也不是我想要的。我知道它为什么不这样做,因为第一个<a>
匹配字符串中的第一个匹配项,并且一直匹配到DEF,然后匹配到最近的结束</a>
。然而,我想要的是匹配最近的开口<a>
和关闭</a>
到“DEF”的方法。所以目前,我得到了这个结果:
<a>TEST</b><a>askldhsfGHIasfklhss</a>
我正在寻找能够得到这个结果的东西:
<a>sdkhfdfojABCasjklhd</a><b>TEST</b><a>askldhsfGHIasfklhss</a>
顺便说一下,我并不想在这里解析HTML,我知道有一些模块可以做到这一点,我只想问一下如何做到这一点。
谢谢, Eric Seifert
答案 0 :(得分:6)
$str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/;
问题是即使使用非贪婪的匹配,Perl仍然试图找到从字符串中最左边可能点开始的匹配。由于.*?
可以匹配<a>
或</a>
,这意味着它始终会找到该行的第一个<a>
。
在开头添加一个贪婪的(.*)
会导致它在行上找到 last 可能的匹配<a>
(因为.*
首先抓住整行,然后回溯直到找到匹配项。)
一个警告:因为它首先找到最右边的匹配,所以不能将此技术与/g
修饰符一起使用。任何其他匹配都在$1
内,/g
恢复上一个匹配结束的搜索,因此无法找到它们。相反,你必须使用如下的循环:
1 while $str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/;
答案 1 :(得分:2)
使用您真正需要的内容而不是一个点:“匹配任何字符”,而不是:“匹配任何不是</a>
的开头的字符” 。这意味着:
$str =~ s/<a>(?:(?!<\/a>).)*DEF(?:(?!<\/a>).)*<\/a>/<b>TEST><\/b>/;
答案 2 :(得分:0)
#!/usr/bin/perl
use warnings;
use strict;
my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>';
my @collections = $str =~ /<a>.*?(ABC|DEF|GHI).*?<\/a>/g;
print join ", ", @collections;
答案 3 :(得分:0)
s{
<a>
(?: (?! </a> ) . )*
DEF
(?: (?! </a> ) . )*
</a>
}{<b>TEST</b>}x;
基本上,
(?: (?! PAT ) . )
相当于
[^CHARS]
表示正则表达式而不是字符。
答案 4 :(得分:0)