2011-04-22 42 views
1

我知道了嗎?操作員啓用「非貪婪」模式,但我遇到了一個問題,我似乎無法繞開。考慮像這樣的字符串:最短匹配問題

my $str = '<a>sdkhfdfojABCasjklhd</a><a>klashsdjDEFasl;jjf</a><a>askldhsfGHIasfklhss</a>'; 

那裏有開始和結束標記<a></a>,有鑰匙ABC,DEF和GHI,但其他一些隨機文本包圍。例如,我想用<b>TEST</b>替換<a>klashsdjDEFasl;jjf</a>。但是,如果我有這樣的事情:

$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,我知道有模塊可以做到這一點,我只是問如何做到這一點。

感謝, 埃裏克·塞弗特

回答

6
$str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/; 

的問題是,即使非貪婪匹配,Perl的仍然試圖找到開始在字符串中最左邊的可能點了比賽。由於.*?可以匹配<a></a>,這意味着它總是會找到第一個<a>就行了。

在開始添加一個貪婪(.*)使其找到最後可能匹配就行了<a>(因爲.*首先抓住全行,然後回溯,直到找到匹配)。

一個警告:因爲它首先找到最右邊的匹配,所以不能在/g修飾符中使用此技巧。任何額外的比賽將在$1之內,並且/g恢復前一場比賽結束的搜索,因此它不會找到它們。相反,你不得不使用像一個循環:

1 while $str =~ s/(.*)<a>.*?DEF.*?<\/a>/$1<b>TEST><\/b>/; 
+0

謝謝,這正是我一直在尋找的。 – 2011-04-22 17:20:01

2

而不是一個點的它說:「匹配任何字符不是:用你真正需要它說「匹配任何字符」</a>」的開頭。這轉化爲這樣的事情:

$str =~ s/<a>(?:(?!<\/a>).)*DEF(?:(?!<\/a>).)*<\/a>/<b>TEST><\/b>/; 
+0

@ysth:感謝逃生...... – ridgerunner 2011-04-22 17:15:31

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; 
+0

你所做的只是改變正則表達式,因此它匹配字符串中出現的所有' ...'。這並不能解決原來的問題,即只匹配其中一組。 – cjm 2011-04-22 17:46:45

+0

啊,你說得對。 @cjm – SymKat 2011-04-22 18:15:38

0
s{ 
    <a> 
    (?: (?! </a>) .)* 
    DEF 
    (?: (?! </a>) .)* 
    </a> 
}{<b>TEST</b>}x; 

基本上,

(?: (?! PAT) .) 

[^CHARS] 

的正則表達式模式,而不是字符等效。