2015-01-02 28 views
1
$regex = "[M]?[VI]?[A]?[R]?[G]?[D]?[LM]?[G]?[IVMAL]?[E]?"; 
$text = "VMVARGDLGVE"; 

if (@matched = $text =~ /$regex/g) { 
    $no_of_match = scalar @matched; 
    print "No of match: ", $no_of_match, "\n"; 
    foreach my $i (@matched) { 
     print $i, "\n"; 
    } 
}  

這個程序如下生成輸出: 不匹配:3 VMV ARGDLGVE的Perl - 提取所有正則表達式匹配

我期待這個程序的輸出是這樣的: V VM VMV MV MVA MVAR MVARG MVARGD MVARGDL MVARGDLG MVARGDLGV MVARGDLGVE ....

我試圖獲得所有可能的匹配作爲輸出。如何獲得所有可能的比賽?

+0

首先'使用strict'和'use warnings'。然後,你有很多不必要的字符類。並考慮使用'while'而不是'if'。 –

+0

這是一個無限循環,如果我使用while .. – Rezwan

+0

噢對不起,你將不得不使用另一個正則表達式。我會研究它。 –

回答

1

這與Count overlapping regex matches in Perl OR Ruby的問題幾乎相同。

此代碼是從的perldoc perlre基本持平,在標題爲「特殊回溯控制動詞」一節:

use strict; 
use warnings; 

my $regex = qr/M?[VI]?A?R?G?D?[LM]?G?[IVMAL]?E?/; 
my $text = 'VMVARGDLGVE'; 

my $count = 0; 
$text =~ /$regex(?{print "$&\n"; $count++})(*FAIL)/g; 
print "Got $count matches\n"; 

腳本並計數空字符串匹配拿出的97場比賽的計數。

+0

優秀使用回溯控制動詞。 – DavidO

+0

有趣的是,您找到了97個匹配項,而我只找到了70個。我們計算的結果稍有不同:如果子字符串以兩種不同的方式匹配,它會計數兩次嗎?目前尚不清楚OP希望的答案。如果你聲明'my%matches;'並在'(?{})'內部添加'$ matches {(pos $ text)。','。$^N} = 1;',那麼'標量鍵%matches'將顯示匹配的子串的數量,它是70或者也許他們甚至不想分別計算重複的子串(例如,只計算一個''''):爲此,使用'$ matches {$^N} = 1 ',並且你得到'標量鍵%匹配'= 56。 –

1

正則表達式匹配不起作用。如果您在找到匹配項後再次搜索,則會從比賽結束開始再次搜索。所以它沒有發現重疊的匹配。

我假設你正在使用Perl 5.我相信在Perl 6中有這樣做的方法。這是一種非常不同的語言。我不知道任何其他語言可以找到像你想要的重疊正則表達式匹配。

它找到的第一個匹配項是開頭的子串VMV。然後它從停止的位置開始搜索,並找到匹配項ARGDLGVE。然後再次從它離開的位置開始嘗試,在這個階段,它位於字符串的末尾。所以它找到最後的空子串作爲匹配。 (請注意,您的正則表達式匹配空字符串。)正則表達式被禁止再次找到相同的空字符串,因爲這會導致無限循環,因此它會停止搜索。

這段代碼有什麼意義?因爲我真的看不到這樣做的好方法,我希望有其他方式可以實現您的目標。你可以遍歷所有可能的子串$text,並使用/^$regex$/分別檢查它們中的每一個。你會發現70個匹配,我的數量(包括重複和空字符串)。也許你想要一個更嚴格的正則表達式?

1

我重構了你的代碼,使其更容易直觀地看到發生了什麼。

my $regex = qr/ 
    M?  # 1: Match M,     greedy, 0 or 1 times. 
    [VI]?  # 2: Match V or I,    greedy, 0 or 1 times. 
    A?  # 3: Match A,     greedy, 0 or 1 times. 
    R?  # 4: Match R,     greedy, 0 or 1 times. 
    G?  # 5: Match G,     greedy, 0 or 1 times. 
    D?  # 6: Match D,     greedy, 0 or 1 times. 
    [LM]?  # 7: Match L or M,    greedy, 0 or 1 times. 
    G?  # 8: Match G,     greedy, 0 or 1 times. 
    [IVMAL]? # 9: Match I, B, M, A or L, greedy, 0 or 1 times. 
    E?  # 10: Match E,     greedy, 0 or 1 times. 
/x; 
my $text = "VMVARGDLGVE"; 

if (my @matched = $text =~ /$regex/gx) { 
    print "No of matches: ", scalar(@matched), "\n"; 
    print "<<$_>>\n" foreach @matched; 
} 

您的正則表達式在儘可能多的方式上匹配,而不會破壞其NFA正則表達式引擎的規則。

  1. 這些規則之一是「最左邊的」。將選擇最接近目標中左側 的子串,以允許總匹配成功。

  2. 的另一條規則是,貪婪的量詞將匹配儘可能 可能的,只會給了他們所擁有的子匹配,如果有必要,讓全場比賽成功 。放棄部分 他們的比賽涉及回溯。除非量詞太多,否則迴避將被避免,並且必須放棄以允許完整匹配成功。

  3. 而另一個規則是,迭代匹配在點 處繼續前一場比賽中止。

透過目標串散步, 「V」 的子模式[VI]?(下文中被稱爲子模式#2)相匹配。貪婪的量詞持有這個'V',並且只有當它被強制在後面時纔會釋放它,以獲得更大的好處。

目標字符串中的「M」與子模式#7匹配。而「V」匹配子模式#9。第一次迭代完成,匹配「VMV」。

現在剩餘的目標字符串看起來像「ARGDLGVE」。 pos標記位於3(目標字符串中的第4個字符),因此第二次迭代的匹配從那裏開始。 'A'匹配於子模式#3,'R'匹配#4,'G'匹配#5,'D'匹配#6,'L'匹配#7,'G'匹配#8,'' V'在#9匹配,'E'在#10匹配。第二次迭代完成後,匹配來自目標字符串的'ARGDLGVE'。

在第三次迭代中,pos標記位於11,位於目標字符串中最後一個字符之後。所以空字符串會與您的正則表達式進行比較。因爲正則表達式中的每個量詞都是「0或1」,所以正則表達式匹配空字符串是可以接受的。所以第三次迭代完成後,匹配「」(空字符串)。

你有三個匹配:「VMV」,「ARGDLGVE」和「」。

你可能希望做的一件事是控制pos標記。在while循環中放置正則表達式,並在循環終止之前,從字符串的起始位置再前進pos。但是這隻能解決你在上述第三條規則中遇到的問題。你仍然會遇到這樣的問題,即量詞從事非常具體的事情,並且不會因爲你認爲它會很方便而違反他們自己的規則。

問題是,正則表達式引擎不是一個置換引擎。它的工作是確定一個給定的目標字符串是否與給定的模式相匹配,遵循一套定義明確的規則(儘管有時會引起混淆)。

我不確定你想要解決的大問題是什麼。如果您只是試圖擴大一系列範圍,那麼CPAN模塊String::Range::Expand可能會取得更好的成效。可能還有其他CPAN模塊可以爲你做範圍擴展,但這可能是一個很好的起點。

+0

感謝您的詳細解釋:) – Rezwan