중첩 된 캡처 그룹은 정규식에서 어떻게 번호가 매겨 집니까?


84

정규식이 중첩 된 괄호의 캡처 동작을 처리하는 방법에 대한 정의 된 동작이 있습니까? 더 구체적으로 말하면, 다른 엔진이 첫 번째 위치의 바깥 쪽 괄호와 후속 위치의 중첩 된 괄호를 캡처 할 것이라고 합리적으로 예상 할 수 있습니까?

다음 PHP 코드를 고려하십시오 (PCRE 정규식 사용).

<?php
  $test_string = 'I want to test sub patterns';
  preg_match('{(I (want) (to) test) sub (patterns)}', $test_string, $matches);
  print_r($matches);
?>

Array
(
    [0] => I want to test sub patterns  //entire pattern
    [1] => I want to test           //entire outer parenthesis
    [2] => want             //first inner
    [3] => to               //second inner
    [4] => patterns             //next parentheses set
)

괄호로 묶인 전체 표현식이 먼저 캡처되고 (테스트하고 싶습니다) 다음으로 안쪽 괄호로 묶인 패턴이 캡처됩니다 ( "want"및 "to"). 이것은 논리적으로 의미가 있지만, 먼저 하위 괄호를 캡처 한 다음 전체 패턴을 캡처하는 경우에도 동일한 논리적 사례가 만들어지는 것을 볼 수 있습니다.

따라서 이것이 정규식 엔진에서 정의 된 동작을 "먼저 전체 캡처"하는 것입니까, 아니면 패턴의 컨텍스트 및 / 또는 엔진의 동작에 따라 달라지는 것인지 (PCRE는 C #이 Java의 것과 다릅니다) 등)?


모든 정규식 버전에 정말로 관심이 있다면 "언어에 구애받지 않는"태그가 원하는 것입니다. 모두를 나열하기에는 너무 많은 풍미가 있으며, 대부분은 실제 표준을 따르지 않습니다 (캡처 그룹 번호 지정에 있어서는 매우 일관 적 임에도 불구하고).
Alan Moore

그룹은 $ 1, $ 2, $ 3 .... 등을 사용하여 액세스 할 수 있습니다. 10 번째 그룹에 액세스하는 방법은 무엇입니까? $ 10일까요? $ 10은 $ 1 다음에 0이 오는 것으로 해석되기 때문에 작동하지 않을 것이라고 생각합니다. 이것은 우리가 최대 9 개의 그룹 만 가질 수 있다는 것을 의미합니까? 작성자가 이것을 질문의 일부로 포함 할 수 있다면 정규 표현식의 중첩 그룹에 대한 모든 것을 알 수있는 단일 장소가 될 것입니다.
LionHeart 2010

답변:


59

에서 perlrequick

정규식의 그룹이 중첩 된 경우 $ 1은 가장 왼쪽 여는 괄호가있는 그룹을 가져오고 $ 2는 다음 여는 괄호 등을 가져옵니다.

주의 사항 : 캡처되지 않은 그룹 여는 괄호 (? =) 제외

최신 정보

나는 일반적으로 실제를 사용하므로 PCRE를 많이 사용하지 않지만 PCRE의 문서 는 Perl과 동일하게 표시됩니다.

SUBPATTERNS

2.서브 패턴을 캡처 서브 패턴으로 설정합니다. 이는 전체 패턴이 일치 할 때 하위 패턴과 일치하는 주제 문자열의 해당 부분이의 ovector인수를 통해 호출자에게 다시 전달됨을 의미합니다 pcre_exec(). 여는 괄호는 왼쪽에서 오른쪽 (1부터 시작)으로 계산되어 캡처하는 하위 패턴의 수를 얻습니다.

예를 들어 문자열 "the red king"이 패턴과 일치하는 경우

the ((red|white) (king|queen))

캡처 된 하위 문자열은 "red king", "red"및 "king"이며 각각 1, 2 및 3으로 번호가 지정됩니다.

PCRE가 Perl 정규식 호환성에서 벗어나면 약어를 재정의해야합니다. "Perl Cognate Regular Expressions", "Perl Comparable Regular Expressions"등입니다. 또는 의미의 문자를 제거하십시오.


1
@Sinan : 그는 "Perl-Compatible Regular Expressions"인 PHP에서 PCRE를 사용하고 있습니다. 그래서 직접 펄을 사용하는 것과 매우 동일해야합니다
파스칼 MARTIN를

3
Pascal, PCRE는 Perl 호환 정규식 집합이되기위한 시도로 시작되었지만 최근 몇 년 동안이 둘은 약간 갈라졌습니다. 여전히 매우 유사하지만 고급 기능 세트에는 미묘한 차이가 있습니다. (또한 질문에 따라 모든 플랫폼에 관심이 있습니다.)
Alan Storm

1
사실, 요즘 대부분의 "표류"를하고있는 것은 Perl이지만, 당신 말이 맞습니다. "Perl 호환"이 잘못된 이름에서 비평 등으로 빠르게 변하고 있습니다. : D
Alan Moore

1
@Alan, Perl은 확실히 이동 중입니다. P5.10은 몇 가지를 변경했지만 6은 크게 다를 것입니다. P는 거의 확실하게 "Perl 5"로 해석되어야합니다. PCRE는 제가 충분히 칭찬 할 수없는 훌륭한 프로젝트입니다. 여러 프로젝트에 대한 신의 선물이었습니다.
daotoad

1
나는 이것을 첫 번째 인용구 아래에 추가했습니다. 주의 사항 : 비 캡처 그룹 여는 괄호 (? =) 제외. 편집 할 때 로그인하지 않았다는 것을 몰랐습니다. 이 설명을 추가 할 때만 자격 증명을 입력하라는 메시지가 표시되었습니다. 이제 승인하려면 1 명이 더 필요합니다!
JGFMK

17

예, 이것은 관심있는 모든 언어에 대해 모두 잘 정의되어 있습니다.

  • Java - http : //java.sun.com/javase/6/docs/api/java/util/regex/Pattern.html#cg
    "캡처 그룹은 여는 괄호를 왼쪽에서 오른쪽으로 세어 번호가 매겨집니다. ... 그룹 0은 항상 전체 표현을 나타냅니다. "
  • .Net - http
    ://msdn.microsoft.com/en-us/library/bs2twtah(VS.71) .aspx "()를 사용하는 캡처는 여는 괄호 순서에 따라 1부터 시작하여 자동으로 번호가 매겨집니다. 캡처, 캡처 요소 번호 0은 전체 정규식 패턴과 일치하는 텍스트입니다. ")
  • PHP (PCRE 기능) - http://www.php.net/manual/en/function.preg-replace.php#function.preg-replace.parameters
    "\ 0 나 $ 0는 전체 패턴에 일치하는 텍스트를 의미합니다. 여는 괄호는 캡처하는 하위 패턴의 번호를 얻기 위해 왼쪽에서 오른쪽 (1부터 시작)으로 계산됩니다. " (사용되지 않는 POSIX 함수도 마찬가지였습니다.)
  • PCRE - http
    ://www.pcre.org/pcre.txt Alan M이 말한 내용에 추가하려면 "How pcre_exec ()가 캡처 된 부분 문자열을 반환하는 방법"을 검색하고 다음 다섯 번째 단락을 읽으십시오.

    정수의 첫 번째 쌍인 ovector [0] 및 ovector [1]은 다음을 식별합니다.
    전체 패턴과 일치하는 제목 문자열의 일부입니다. 다음
    쌍은 첫 번째 캡처 하위 패턴 등에 사용됩니다. 가치
    pcre_exec ()에 의해 반환되는 것은
    설정되었습니다. 예를 들어 두 개의 하위 문자열이 캡처 된 경우
    반환 된 값은 3입니다. 캡처하는 하위 패턴이없는 경우
    성공적인 일치의 값은 1이며 첫 번째 쌍만
    오프셋 수가 설정되었습니다.
    
  • 펄의 다른 - http://perldoc.perl.org/perlre.html#Capture-buffers
    $ 1, $ 2 등 예상대로 그룹을 캡처 일치 (예 : 여는 괄호의 발생에 의해), 그러나 $ 0을 반환 프로그램 이름이 아닌 전체 쿼리 문자열-대신 $ &를 사용합니다.

다른 언어 (Python, Ruby 등)에서도 유사한 결과를 찾을 수 있습니다.

먼저 내부 캡처 그룹을 나열하는 것이 똑같이 논리적이라고 말하고 맞습니다. 괄호를 여는 것이 아니라 닫을 때 인덱싱하는 문제 일뿐입니다. (내가 올바르게 이해한다면). 하지만 이렇게하는 것은 덜 자연스럽고 (예를 들어 읽기 방향 규칙을 따르지 않음) 따라서 어떤 캡처 그룹이 주어진 결과 인덱스에있을 것인지를 검색하여 결정하기가 더 어렵게 만듭니다 (아마 중요하지 않을 수도 있음).

전체 일치 문자열을 위치 0에 두는 것도 대부분 일관성을 위해 의미가 있습니다. 정규식에서 정규식으로 그룹을 캡처하는 수와 실제로 일치하는 캡처 그룹의 수에 관계없이 일치하는 전체 문자열이 동일한 인덱스에 유지되도록 허용합니다 (예 : Java는 각 캡처에 대해 일치하는 그룹 배열의 길이를 축소합니다). group은 어떤 콘텐츠와도 일치하지 않습니다 (예 : "a (. *) pattern"과 같은 것을 생각해보십시오). 항상 capturing_group_results [capturing_group_results_length-2]를 검사 할 수는 있지만 변수를 동적으로 생성하는 Perl 언어로 잘 변환되지 않습니다 ($ 1 , $ 2 등) (Perl은 일치하는 표현식에 $ &를 사용하기 때문에 당연히 나쁜 예입니다.하지만 여러분은 아이디어를 얻었습니다. :).


1
좋은 대답 ..하지만 파이썬 (2 & 3) 업데이트도
어떻습니까

JavaScript는 어떻습니까!?!
mesqueeb

9

내가 아는 모든 정규식 풍미는 여는 괄호가 나타나는 순서에 따라 번호 그룹을 지정합니다. 외부 그룹이 포함 된 하위 그룹보다 먼저 번호가 매겨지는 것은 명시적인 정책이 아닌 자연스러운 결과입니다.

흥미로운 부분은 명명 된 그룹 입니다. 대부분의 경우 괄호의 상대적 위치에 따라 번호를 매기는 동일한 정책을 따릅니다. 이름은 번호의 별칭 일뿐입니다. 그러나 .NET 정규식에서 명명 된 그룹은 번호가 매겨진 그룹과 별도로 번호가 지정됩니다. 예를 들면 :

Regex.Replace(@"one two three four", 
              @"(?<one>\w+) (\w+) (?<three>\w+) (\w+)",
              @"$1 $2 $3 $4")

// result: "two four one three"

실제로 번호이름 의 별칭입니다 . 명명 된 그룹에 할당 된 번호는 "실제"번호가 매겨진 그룹이 제외 된 곳에서 시작됩니다. 그것은 기괴한 정책처럼 보일 수 있지만 그럴만 한 이유가 있습니다. .NET 정규식에서는 정규식에서 동일한 그룹 이름을 두 번 이상 사용할 수 있습니다. 따라서 다른 로케일의 부동 소수점 숫자를 일치시키기 위해이 스레드의 것과 같은 정규식을 사용할 수 있습니다.

^[+-]?[0-9]{1,3}
(?:
    (?:(?<thousand>\,)[0-9]{3})*
    (?:(?<decimal>\.)[0-9]{2})?
|
    (?:(?<thousand>\.)[0-9]{3})*
    (?:(?<decimal>\,)[0-9]{2})?
|
    [0-9]*
    (?:(?<decimal>[\.\,])[0-9]{2})?
)$

천 단위 구분 기호가있는 경우 정규식의 어느 부분이 일치하는지에 관계없이 "천"그룹에 저장됩니다. 마찬가지로 소수점 구분 기호 (있는 경우)는 항상 "decimal"그룹에 저장됩니다. 물론 재사용 가능한 명명 그룹없이 구분 기호를 식별하고 추출하는 방법이 있지만이 방법이 훨씬 더 편리하여 이상한 번호 매기기 체계를 정당화하는 것 이상이라고 생각합니다.

그리고 Perl 5.10+는 제가 무엇을해야할지 아는 것보다 그룹 캡처를 더 잘 제어 할 수있게 해줍니다. :디


4

왼쪽 괄호 순서로 캡처하는 순서는 내가 작업 한 모든 플랫폼에서 표준입니다. (perl, php, ruby, egrep)


"왼쪽 괄호 순서대로 캡처"감사합니다. 이것은 동작을 훨씬 더 간결하게 설명하는 방법입니다.
Alan Storm

1
Perl 5.10 및 Perl 6에서 캡처 번호를 다시 지정할 수 있습니다.
Brad Gilbert
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.