2011-11-09 9 views
2

私はいくつかのテキストと照合するのに必要な正規表現のリスト(約10-15)を持っています。ループ内でそれらを1つずつ照合するのは遅すぎます。しかし、一度にすべての正規表現にマッチする自分の状態マシンを書くのではなく、私は|個の正規表現にしようとしており、perlに作業をさせています。問題は、代替案のどれが一致したかをどのようにして知るのですか?どのような代替案がPerlの正規表現パターンで一致するかをどのようにして知ることができますか?

この質問は、個々の正規表現の内部にキャプチャグループがない場合に対処しています。 (which portion is matched by regex?)各正規表現の内部にキャプチャグループがある場合はどうなりますか?以下とそう

/^(A(\d+))|(B(\d+))|(C(\d+))$/ 

と、文字列 "A123"、どのように私は両方のA123が一致したことを知っていて、 "123" を抽出することができますか?

答えて

5

正規表現を結合するために独自のステートマシンをコード化する必要はありません。 Regexp:Assembleをご覧ください。それはあなたの最初のパターンのどれが一致したかを追跡するメソッドを持っています。

編集:あなたの例のデータでは

use strict; 
use warnings; 

use 5.012; 

use Regexp::Assemble; 

my $string = 'A123'; 

my $re = Regexp::Assemble->new(track => 1); 
for my $pattern (qw/ A(\d+) B(\d+) C(\d+) /) { 
    $re->add($pattern); 
} 

say $re->re; ### (?-xism:(?:A(\d+)(?{0})|B(\d+)(?{2})|C(\d+)(?{1}))) 
say for $re->match($string); ### A(\d+) 
say for $re->capture; ### 123 
+0

がすばらしく見えます。 – dividebyzero

+0

モジュールのREADMEの "Tracking"セクションに必要なものが見つかりました。 - 私は特定のパターンマッチで派遣する必要があった。素晴らしいツール。 – dividebyzero

2

A123は、キャプチャグループ$1123になりますグループに$2

になりますだからあなたが言うことができる:

if (/^(A(\d+))|(B(\d+))|(C(\d+))$/ && $1 eq 'A123' && $2 eq '123') { 
    ... 
} 

これは冗長ですが、あなたのアイデアを得る...

EDIT:いいえ、それぞれの部分一致を列挙する必要はありません。A123と一致しているかどうかを知る方法を教えてください道123

  • あなたはA123一致
  • ない限りifブロックに入らないだろうし、あなたが$2後方参照を使用して123を抽出することができます。

    if (/^(A(\d+))|(B(\d+))|(C(\d+))$/) { 
        # do something with $2, which will be '123' assuming $_ matches /^A123/ 
    } 
    

    はEDIT 2:

    別の質問である(AoAの中で試合をキャプチャするには、これはそれを行う必要がありますので、多分この例では、より明確にされているだろう

):

#!/usr/bin/perl 
use strict; 
use warnings; 
use Data::Dumper; 

my @matches = map { [$1,$2] if /^(?:(A|B|C)(\d+))$/ } <DATA>; 
print Dumper \@matches; 

__DATA__ 
A123 
B456 
C769 

結果:

私はあなたの正規表現を変更し、それはあなたがあなたのコメントから判断するために行っているものだように見える210
$VAR1 = [ 
      [ 
      'A', 
      '123' 
      ], 
      [ 
      'B', 
      '456' 
      ], 
      [ 
      'C', 
      '769' 
      ] 
     ]; 

注...

+0

だから私は手動で各正規表現の括弧の数を列挙する必要がありますか? @groups =($ _ =〜/ ^(A(\ d +))|(B(\ d +))|(C(\ d +))$ /)と$のような配列のようなものを期待していました。 group [1] - > [0]は "123"と一致します。ありえない? – dividebyzero

+0

各サブ正規表現の中に任意の数のキャプチャグループがある場合、$ 3(例えば)とマッチすると、どの$を最初に見つけ出すことなく一致したサブ正規表現が見つけられないでしょうか?どの捕獲に対応するか。 – dividebyzero

+0

@dividebyzero、これは別の質問ですが、EDIT 2 ... – MisterEd

5

/^ (?<prefix> A|B|C) (?<digits> \d+) $/xを使用しない理由。注意を要するのは、分かりやすくするためのキャプチャグループであり、必須ではありません。

1

、$ 1は、プレフィックスと$ 2のサフィックスが含まれていますそのあと

'A123' =~ /^([ABC])(\d+)$/; 

を書くのは簡単です。

これはあなたの実際のデータと関連があるのか​​どうかはわかりませんが、追加のモジュールを使用することは過度のようです。

+0

ABC 123はほんの一例にすぎず、実行時にほかのモジュールから読み込んだパターンはもっと複雑です。したがって、Regexp :: Assembleを使用することは正当化されます。 – dividebyzero

0

Perlで行うことのできるもう1つのことは、 "(?{...})"を使ってPerlコードを正規表現に直接埋め込むことです。したがって、正規表現のどの部分がマッチしたかを示す変数を設定することができます。警告:あなたの正規表現には、正規表現に補間される(埋め込まれたPerlコード以外の)変数は含まれていないか、エラーが発生します。

my $kind; 
my $REGEX = qr/ 
      [A-Za-z][\w]*      (?{$kind = 'IDENT';}) 
     | (?: ==? | != | <=? | >=?)   (?{$kind = 'OP';}) 
     | -?\d+        (?{$kind = 'INT';}) 
     | \x27 ((?:[^\x27] | \x27{2})*) \x27 (?{$kind = 'STRING';}) 
     | \S         (?{$kind = 'OTHER';}) 
     /xs; 

my $line = "if (x == 'that') then x = -23 and y = 'say ''hi'' for me';"; 
my @tokens; 
while ($line =~ /($REGEX)/xsg) { 
    my($match, $str) = ($1,$2); 
    if ($kind eq 'STRING') { 
     $str =~ s/\x27\x27/\x27/g; 
     push(@tokens, ['STRING', $str]); 
     } 
    else { 
     push(@tokens, [$kind, $match]); 
     } 
    } 
foreach my $lItems (@tokens) { 
    print("$lItems->[0]: $lItems->[1]\n"); 
    } 

次出力します:ここでは、この機能使用するサンプルパーサーです

IDENT: if 
OTHER: (
IDENT: x 
OP: == 
STRING: that 
OTHER:) 
IDENT: then 
IDENT: x 
OP: = 
INT: -23 
IDENT: and 
IDENT: y 
OP: = 
STRING: say 'hi' for me 
OTHER: ; 

それは一種の不自然だが、しかし、あなたはわかりますその文字列の前後に引用符(実際には、アポストロフィ) (また、連続した引用符は一重引用符に畳まれています)、一般的に、$ kind変数だけが、パーサが識別子や引用符付きの文字列を見たかどうかを示します。

関連する問題