2012-12-03 87 views
3

如何獲得正則表達式中最低的匹配組號?如何獲得最低匹配的captureno?

假設有一個正則表達式

/(a(b))|(b(1))|(c(4))/... 

例如輸入字符串爲 「B1」,最低的匹配組無2.($ 2)

例如輸入字符串爲 「C4」,最低的匹配組沒有5.($ 5)

例如輸入字符串爲「AB」,最低的匹配組無1.($ 1)

我有一個解決方案,但它不是很有效。 Thakns所有的嘗試。 真正的問題是效率。許多人提供了類似的解決方案。 問題是線性時間搜索最低組。 O(N)其中n是捕獲組的數量。 我想知道是否有更快的方法。 O(1)這是這個問題的目的。 我預計Perl有一個隱藏功能來獲得該值。我想沒有。

同時我發現溶液自己,在這裏它是..

/(A(B)(?? {$第一= 1; 「」}))|(B(1) (?? {$ first = 2;「」}))|(c(4)(?? {$ first = 5;「」}))/

找出$ first的時間是O(1 )。

if (@matches = $conv::content =~/$conv::trtree{convertsourceregqr}[$conversionno]/) 
     { 

     my $firstno; 
     my $c = 0; 
     for my $m (@matches) 
     { 
      if (defined $m) 
      { 
      $firstno=$c; 
      last; 
      } 
      $c++; 
     }**strong text****strong text** 
+3

你是什麼意思的「最低匹配組」? – Toto

+0

正則表達式中的代碼塊是[實驗性的](http://perldoc.perl.org/perlre.html#Extended-Patterns)。我已經添加了一個更新[我的答案](http://stackoverflow.com/a/13689587/468327),根據你的用例,它('(?|)')或命名捕獲應該做你需要的不用擔心使用實驗性功能。 –

回答

0

正則表達式中的組編號是parens的編號。

1 2 3 4 5 6 
/(a(b))|(b(1))|(c(4))/ 

快速腳本來證明這一點:

#!/usr/bin/perl 

foreach my $v ('ab', 'b1', 'c4') { 
    $v =~ /(a(b))|(b(1))|(c(4))/; 
    if(defined $1) { print "One!\n"; } 
    if(defined $3) { print "Three!\n"; } 
    if(defined $5) { print "Five!\n"; } 
    print << "--EOB--"; 
$v 
1 $1 
2 $2 
3 $3 
4 $4 
5 $5 
6 $6 

--EOB-- 
} 

它產生的輸出:

One! 
ab 
1 ab 
2 b 
3 
4 
5 
6 

Three! 
b1 
1 
2 
3 b1 
4 1 
5 
6 

Five! 
c4 
1 
2 
3 
4 
5 c4 
6 4 

在這一點上,應該能夠很容易地修改代碼來爲他們做什麼無論哪個組匹配。

2

存儲在數組中的匹配項,並查找該第一規定值的索引:

my $str = 'c4'; 
my @matches = ($str =~ m/(a(b))|(b(1))|(c(4))/); 
for my $i (0..$#matches) { 
    if (defined $matches[$i]) { 
     printf "First matching group: %d\n", $i+1; 
     last; 
    } 
} 
# output: 5 

注意,這決不會輸出2,4或6個自組1,3或5必須在一個相匹配他們匹配。

如果你只想要第一個匹配組的內容:

use List::Util 'first'; 
my $str = 'c4'; 
print first { defined } $str =~ m/(a(b))|(b(1))|(c(4))/; 
1

的特殊變量@- and @+持有的起點和成功匹配的結束位置。您的問題的實際應用是,如果$<n>保持一定的價值($<n>$1,$2等),那麼$+[<n>]將大於$-[<n>]

for ('b1', 'c4', 'ab') { 

    /(a(b))|(b(1))|(c(4))/; 
    my @i = grep { $+[$_] > $-[$_] } 1..$#+; 

    # @i contains list of successful matches, 
    # i.e., if @i == (3,4), then $3 and $4 contain values 
    if (@i > 0) { 
     print "Earliest match for '$_' is: \$$i[0]\n"; 
    } else { 
     print "No match for '$_'\n"; 
    } 
} 
4

這並不特別符合你的問題,但它可能會解決您的實際問題(或者未來的讀者)。

編輯(12年12月10日):

還有一個選項,the special construct (?|)將重組編號的交替,使數字將是一致的。這無助於確定哪個組匹配,但會向您保證這些匹配位於$1$2之間。如果您需要知道哪些匹配,命名捕獲(下面)是要走的路。

#!/usr/bin/env perl 

use strict; 
use warnings; 

foreach my $v ('ab', 'b1', 'c4') { 
    print "Input: $v\n"; 
    next unless $v =~ /(?|(a(b))|(b(1))|(c(4)))/; 
    print "$1 => $2\n"; 
} 

原始 也許你想使用named captures來便於理解什麼匹配的負擔。命名的捕獲結果放置在%+散列中,因此更容易反思。

#!/usr/bin/env perl 

use strict; 
use warnings; 

foreach my $v ('ab', 'b1', 'c4') { 
    print "Input: $v\n"; 
    next unless $v =~ /(?<a>a(?<ab>b))|(?<b>b(?<b1>1))|(?<c>c(?<c4>4))/; 
    foreach my $key (sort keys %+) { 
    next unless defined $+{$key}; 
    print "\t$key => $+{$key}\n"; 
    } 
} 

打印

Input: ab 
    a => ab 
    ab => b 
Input: b1 
    b => b1 
    b1 => 1 
Input: c4 
    c => c4 
    c4 => 4 

編輯

其實,像這樣的交替,也許你想簡單地使用重複的名字!

#!/usr/bin/env perl 

use strict; 
use warnings; 

foreach my $v ('ab', 'b1', 'c4') { 
    print "Input: $v\n"; 
    next unless $v =~ /(?<outer>a(?<inner>b))|(?<outer>b(?<inner>1))|(?<outer>c(?<inner>4))/; 
    print "\touter => $+{outer}\n"; 
    print "\tinner => $+{inner}\n"; 
} 

打印

Input: ab 
    outer => ab 
    inner => b 
Input: b1 
    outer => b1 
    inner => 1 
Input: c4 
    outer => c4 
    inner => 4 
1

首先,使用括號這樣令人困惑。這一特定問題的最簡單的解決方法就是使用一個:

/(ab|b1|c4)/ 

由於其它括號不服務在這種特殊情況下有目的的,這會工作。

但是,有時候需要分組,在這種情況下,您可以使用非捕獲括號並使用一個來捕獲,(?: ...)。在你的情況下,它看起來像這樣:

/((?:a(?:b))|(?:b(?:1))|(?:c(?:4)))/ 
+0

混淆它可能是,我使用括號來確定匹配。否則,我不會使用它們。 – Aftershock

+0

@餘震那麼,在這種情況下,我會建議使用單獨的正則表達式。它會更容易閱讀,維護起來更容易,並且可能不易出現錯誤。如果您需要確定匹配的內容,那麼事後查看它很簡單。這在我看來是一個尷尬和不穩定的解決方案。 – TLP

+0

正則表達式是從一組字符串自動生成的。確實這很醜陋,但我認爲這是因爲正則表達式優化而以這種方式更快。 – Aftershock