2011-11-09 4 views
2

일부 텍스트와 일치해야하는 정규식 목록 (약 10-15)이 있습니다. 루프에서 하나씩 일치 시키려면 너무 느립니다. 하지만 모든 regexes를 한 번에 맞추기 위해 내 상태 머신을 작성하는 대신, | 개개의 정규식을 시도하고 perl이 작업을 수행하게하려고합니다. 문제는 대안 중 어느 것이 일치하는지 어떻게 알 수 있습니까?대안 중 어느 것이 펄 정규 표현 패턴과 일치하는지 어떻게 알 수 있습니까?

이 질문은 개별 정규식 안에 캡처 그룹이없는 경우를 다룹니다. (which portion is matched by regex?) 각 regexes 안에 캡쳐 그룹이 있다면? 다음과

그래서

,

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

및 문자열 "A123은"어떻게 모두 A123은 "123"일치와 추출 있음을 알 수 있는가?

답변

5

regexes를 결합하기 위해 자신의 상태 시스템을 코딩 할 필요가 없습니다. 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 캡처 그룹 $1에있을 것입니다 및 123$2

그래서 당신이 말할 수있는 그룹에있을 것입니다 ...

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

이 중복입니다,하지만 당신은 아이디어를 얻을

편집 : 아니요, 각 하위 경기를 열거 할 필요가 없으며 A123과 일치하는지 여부를 알 수있는 방법과 전 방법 기관 123는 :

  • 당신은 if 블록을 입력하지 않습니다 일치 A123
  • 하지 않는 한 당신은 $2 역 참조를 사용하여 123을 추출 할 수 있습니다.

그래서 어쩌면이 예제는 더 명확했을 것이다 :

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

편집 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 

결과 :

,691,363 나는 당신의 정규식을 수정,하지만 당신은 당신의 코멘트에 의해 판단하는 건지처럼 보이는210
$VAR1 = [ 
      [ 
      'A', 
      '123' 
      ], 
      [ 
      'B', 
      '456' 
      ], 
      [ 
      'C', 
      '769' 
      ] 
     ]; 

주 ...

+0

그래서 각 하위 정규식에서 괄호의 수를 수동으로 계산해야합니까? @groups = ($ _ = ~/^ (A (\ d +)) | (B (\ d +)) | (C (\ d +)) $ /)와 같은 배열 내부의 배열과 같은 것을 기대했다. group [1] -> [0]은 "123"과 일치합니다. 가능하지 않습니까? – dividebyzero

+0

각 sub regex 안에 임의의 숫자를 포착하는 그룹이있는 경우 $ 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에서 할 수있는 또 다른 일은 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 변수 만이 파서가 식별자 나 따옴표 붙은 문자열을 보았는지 알 수있다.

관련 문제