정규식(PCRE) PCRE Patterns Once-only subpatterns

반복을 최대화하고 최소화할 때 일반적으로 다음 항목이 실패하면 반복 항목이 다른 횟수의 반복이 나머지 패턴과 일치하도록 허용하는지 확인하기 위해 반복 항목을 다시 평가합니다. 패턴 작성자가 계속하는 것이 의미가 없음을 알고 있을 때 일치의 특성을 변경하거나 그렇지 않은 경우보다 일찍 실패하게 하여 이를 방지하는 것이 때때로 유용합니다.

예를 들어 제목 줄 123456bar에 적용될 때 \d+foo 패턴을 고려하십시오.

6자리 숫자를 모두 일치시킨 다음 "foo" 일치에 실패한 후, matcher의 일반적인 동작은 \d+ 항목과 일치하는 5자리 숫자로만 다시 시도한 다음 4를 사용하여 궁극적으로 실패하기 전에 다시 시도하는 것입니다. Once-only 하위 패턴은 패턴의 일부가 일치하면 이러한 방식으로 재평가되지 않도록 지정하는 수단을 제공하므로 매처는 처음 "foo" 일치에 실패하면 즉시 포기합니다. 표기법은 이 예에서와 같이 (?>로 시작하는 특수 괄호의 또 다른 종류입니다. (?>\d+)bar

이러한 종류의 괄호는 일치하면 포함된 패턴의 일부를 "고정"하고 패턴에 대한 추가 오류가 해당 패턴으로 역추적하는 것을 방지합니다. 그러나 이전 항목으로 되돌아가는 것은 정상적으로 작동합니다.

대체 설명은 이 유형의 하위 패턴이 주제 문자열의 현재 지점에 고정된 경우 동일한 독립형 패턴이 일치하는 문자열과 일치한다는 것입니다.

한 번만 사용하는 하위 패턴은 하위 패턴을 캡처하지 않습니다. 위의 예와 같은 간단한 경우는 가능한 모든 것을 삼켜야 하는 최대화 반복으로 생각할 수 있습니다. 따라서 \d+와 \d+ 모두? 나머지 패턴이 일치하도록 하기 위해 일치하는 자릿수를 조정할 준비가 되어 있습니다. (?>\d+)는 전체 자릿수 시퀀스만 일치시킬 수 있습니다.

물론 이 구성은 임의로 복잡한 하위 패턴을 포함할 수 있으며 중첩될 수 있습니다.

한 번만 사용할 수 있는 하위 패턴을 lookbehind 어설션과 함께 사용하여 주제 문자열 끝에 효율적인 일치를 지정할 수 있습니다. 일치하지 않는 긴 문자열에 적용될 때 abcd$와 같은 간단한 패턴을 고려하십시오. 일치가 왼쪽에서 오른쪽으로 진행되기 때문에 PCRE는 주제에서 각 "a"를 찾은 다음 다음에 나오는 내용이 나머지 패턴과 일치하는지 확인합니다. 패턴이 ^.*abcd$로 지정되면 초기 .*는 처음에 전체 문자열과 일치하지만 이것이 실패하면(뒤에 "a"가 없기 때문에) 마지막 문자를 제외한 모든 문자와 일치하도록 역추적한 다음 모든 문자열과 일치합니다. 그러나 마지막 두 문자 등. 다시 한 번 ""에 대한 검색은 전체 문자열을 오른쪽에서 왼쪽으로 포함하므로 더 나은 상황이 아닙니다. 그러나 패턴이 ^(?>.*)(?<=abcd)로 작성되면 .* 항목에 대한 역추적이 없을 수 있습니다. 전체 문자열과만 일치할 수 있습니다. 후속 lookbehind 어설션은 마지막 4개 문자에 대해 단일 테스트를 수행합니다. 실패하면 매치가 즉시 실패합니다. 긴 문자열의 경우 이 접근 방식은 처리 시간에 상당한 차이를 만듭니다.

패턴 자체가 무제한 반복될 수 있는 하위 패턴 내부에 무제한 반복이 포함되어 있는 경우 한 번만 사용하는 하위 패턴을 사용하는 것이 실제로 매우 오랜 시간이 걸리는 일부 실패한 일치를 피할 수 있는 유일한 방법입니다. (\D+|<\d+>)*[!?] 패턴은 숫자가 아니거나 <>로 묶인 숫자로 구성되고 그 뒤에 ! 또는 ?. 일치하면 빠르게 실행됩니다. 그러나 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa에 적용되면 실패보고까지 오랜 시간이 걸립니다. 이는 문자열이 두 개의 반복 사이에서 여러 가지 방법으로 분할될 수 있고 모두 시도해야 하기 때문입니다. (예제는 끝에 단일 문자가 아닌 [!?]를 사용했습니다. PCRE와 Perl 모두 단일 문자를 사용할 때 빠른 실패를 허용하는 최적화를 가지고 있기 때문입니다. 그들은 일치에 필요한 마지막 단일 문자를 기억하고, 문자열에 없으면 일찍 실패합니다.) 패턴이 ((?>\D+)|<\d+>)*[!?] 로 변경되면 숫자가 아닌 시퀀스가 ​​끊어지지 않고 실패가 빨리 발생합니다.