3

如何在正则表达式中获得最低匹配组号?

假设有一个正则表达式

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

例如输入字符串是 "b1" ,最低匹配组没有 2. ($2)

例如输入字符串是 "c4" ,最低匹配组没有 5. ($5)

例如输入字符串是 "ab" ,最低匹配组没有 1. ($1)

我有一个解决方案,但效率不高。谢谢大家的尝试。真正的问题是效率。许多人提供了我发现的类似解决方案。问题是线性时间搜索最低组。O(N) 其中 n 是捕获组的数量。我想知道是否有更快的方法。O(1) 这就是这个问题的目的。我希望 Perl 有一个隐藏的特性来获得这个值。我想没有。

同时我自己找到了解决方案在这里..

/(a(b)(??{ $first=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**
4

5 回答 5

4

这与您的问题并不完全匹配,但它可能会解决您的实际问题(或者未来读者的问题)。

编辑(12 年 12 月 10 日):

另一种选择是,特殊构造(?|)将交替重新组织编号,以使数字保持一致。这无助于识别匹配的组,但会确保匹配项位于$1and中$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";
}

原创 也许您想使用命名捕获来减轻理解匹配内容的负担。命名的捕获结果被放置在%+散列中,因此更容易自省。

#!/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
于 2012-12-03T18:40:08.253 回答
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))/;
于 2012-12-03T18:29:53.477 回答
1

特殊变量@-and@+保存成功匹配的开始和结束位置。对您的问题的实际应用是,如果$<n>具有某些值(对于$<n>in $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";
    }
}
于 2012-12-03T18:31:19.210 回答
1

首先,以这种方式使用括号是令人困惑的。这个特定问题的最简单的解决方案是只使用一个:

/(ab|b1|c4)/

由于其他括号在这种特殊情况下不起作用,因此这将起作用。

但是,有时可能需要分组,在这种情况下,您可以使用非捕获括号并只使用一个来捕获,(?: ... ). 在您的情况下,它看起来像这样:

/((?:a(?:b))|(?:b(?:1))|(?:c(?:4)))/
于 2012-12-03T19:15:29.277 回答
0

正则表达式中的组编号是括号的数量。

 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

此时,应该能够轻松地修改代码以针对匹配的任何组执行任何操作。

于 2012-12-03T18:20:10.457 回答