3

正規表現で最も一致しないグループ番号を取得するにはどうすればよいですか?

正規表現があるとします

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

例: 入力文字列は "b1" で、一致するグループ番号 2 の最小値です。 ($2)

例: 入力文字列は "c4" で、一致するグループ番号 5 の最小値です。 ($5)

例: 入力文字列は "ab" で、一致するグループ番号 1 の最小値です。 ($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/10/12):

もう 1 つのオプションとして、特別な構造(?|)が交互に番号付けを再編成し、番号が一貫するようにします。これは、どのグループが一致したかを特定するのには役立ちませんが、一致が と にあることを保証し$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";
}

オリジナルおそらく、名前付きキャプチャを使用して、何が一致したかを理解する負担を軽減し たいと思うでしょう。名前付きのキャプチャ結果は%+ハッシュに配置されるため、イントロスペクトがはるかに簡単になります。

#!/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

グループ 1、3、または 5 のいずれかが一致するにはグループ 1、3、または 5 が一致する必要があるため、これは 2、4、または 6 を出力しないことに注意してください。

最初に一致したグループのコンテンツのみが必要な場合:

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

特殊変数@-@+は、成功した一致の開始位置と終了位置を保持します。あなたの質問への実際の適用は、$<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)/

この特定のケースでは他の括弧は目的を果たさないので、これは機能します。

ただし、グループ化が必要な場合があります。その場合、非キャプチャ括弧を使用して、1 つだけを使用してキャプチャできます(?: ... )。あなたの場合、次のようになります。

/((?: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 に答える