a ^ nb ^ n을 Java 정규식과 어떻게 일치시킬 수 있습니까?


99

이것은 교육용 정규식 기사 시리즈의 두 번째 부분입니다. 비정규 언어 a n b n 을 일치시키기 위해 미리보기 및 중첩 참조를 사용하는 방법을 보여줍니다 . 중첩 참조는 처음 도입되었습니다. 이 정규식은 삼각형 숫자를 어떻게 찾습니까?

전형적인 비정규 언어 중 하나 는 다음과 같습니다.

L = { an bn: n > 0 }

이것은 일부 수의 a's와 동일한 수의 b's 로 구성된 모든 비어 있지 않은 문자열의 언어입니다 . 이 언어에서 문자열의 예는 ab, aabb, aaabbb.

이 언어는 펌핑 기본형에 의해 비정규적인 것으로 보일 수 있습니다 . 사실 문맥 자유 문법에 의해 생성 될 수 있는 전형적인 문맥 자유 언어 입니다. S → aSb | ab

그럼에도 불구하고 현대의 정규식 구현은 일반 언어 이상의 것을 분명히 인식합니다. 즉, 공식 언어 이론 정의에 의해 "일반적"이지 않습니다. PCRE 및 Perl은 재귀 정규식을 지원하고 .NET은 균형 그룹 정의를 지원합니다. 예를 들어 역 참조 일치와 같은 덜 "멋진"기능은 정규식이 규칙적이지 않음을 의미합니다.

그러나이 "기본"기능이 얼마나 강력합니까? L예를 들어 Java 정규식으로 인식 할 수 있습니까 ? 우리는 아마도 lookarounds 및 중첩 된 참조를 결합하는 예와 작품을하는 패턴을 가질 수 String.matches와 같은 문자열을 일치하도록 ab, aabb, aaabbb, 등?

참고 문헌

연결된 질문


4
이 시리즈는 일부 커뮤니티 ( meta.stackexchange.com/questions/62695/… ) 의 허가를 받아 시작되었습니다 . 수신이 좋으면 정규식의 기본 기능뿐만 아니라 다른 고급 기능도 계속해서 다룰 계획입니다.
polygenelubricants


와우, Java의 정규식이 정규식으로 제한되지 않는다는 것을 결코 알지 못했습니다. 그것이 내가 항상 완전히 구현되지 않을 것이라고 생각한 이유를 설명하는 것 같습니다. 내가 의미하는 것은 Java Regex에 내장 된 보완, 차이 또는 제품 연산자가 없다는 것입니다.하지만 정규 언어로 제한되지 않기 때문에 의미가 있습니다.
Lan

이 질문은 "Advanced Regex-Fu"아래 의 Stack Overflow Regular Expression FAQ 에 추가되었습니다 .
aliteralmind

답변:


139

대답은 말할 필요도없이 YES입니다! n b n 과 일치 하는 Java 정규식 패턴을 가장 확실하게 작성할 수 있습니다 . 어설 션에는 긍정적 인 예견을 사용하고 "카운팅"에는 하나의 중첩 참조를 사용합니다.

이 답변은 즉시 패턴을 제공하는 대신 독자에게 패턴 을 유도 하는 과정 을 안내 합니다. 솔루션이 천천히 구성됨에 따라 다양한 힌트가 제공됩니다. 이 측면에서 바라건대이 답변은 다른 깔끔한 정규식 패턴 이상을 포함 할 것입니다. 독자들이 "정규식으로 생각"하는 방법과 다양한 구성을 조화롭게 조합하는 방법을 배우면 앞으로 스스로 더 많은 패턴을 도출 할 수 있기를 바랍니다.

솔루션 개발에 사용되는 언어는 간결함을 위해 PHP입니다. 패턴이 완성되면 최종 테스트는 Java로 수행됩니다.


1 단계 : 어설 션에 대한 예측

더 간단한 문제로 시작 해보자 : 우리 a+는 문자열의 시작 부분에서 일치 를 원 하지만 바로 뒤에 b+. 우리가 사용 ^하는 앵커 우리의 일치, 우리는 단지를 일치시킬 이후 a+를 빼고 b+, 우리가 사용할 수 있습니다 내다 주장을 (?=…).

다음은 간단한 테스트 도구를 사용한 패턴입니다.

function testAll($r, $tests) {
   foreach ($tests as $test) {
      $isMatch = preg_match($r, $test, $groups);
      $groupsJoined = join('|', $groups);
      print("$test $isMatch $groupsJoined\n");
   }
}
 
$tests = array('aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb');
 
$r1 = '/^a+(?=b+)/';
#          └────┘
#         lookahead

testAll($r1, $tests);

출력은 다음과 같습니다 ( ideone.com에 표시됨 ).

aaa 0
aaab 1 aaa
aaaxb 0
xaaab 0
b 0
abbb 1 a

이것은 정확히 우리가 원하는 출력 a+입니다. 문자열의 시작 부분에 있고 바로 뒤에 오는 경우에만 일치 합니다 b+.

Lesson : 어설 션을 만들기 위해 lookaround에서 패턴을 사용할 수 있습니다.


2 단계 : 미리보기 (및 자유 간격 모드)에서 캡처

이제 b+경기의 일부가되는 것을 원하지 않더라도 어쨌든 그룹 1로 캡처 하고 싶다고 가정 해 봅시다. 또한 더 복잡한 패턴이있을 것으로 예상 하므로 자유 간격에x modifier를 사용 하겠습니다. 정규식을 더 읽기 쉽게 만들 수 있습니다.

이전 PHP 스 니펫을 기반으로 이제 다음과 같은 패턴이 있습니다.

$r2 = '/ ^ a+ (?= (b+) ) /x';
#                └──┘ 
#                  1  
#             └────────┘
#              lookahead
 
testAll($r2, $tests);

이제 출력은 다음과 같습니다 ( ideone.com에 표시됨 ).

aaa 0
aaab 1 aaa|b
aaaxb 0
xaaab 0
b 0
abbb 1 a|bbb

예를 들어 각 그룹이으로 캡처 한 -ing aaa|b결과입니다 . 이 경우 그룹 0 (즉 , 일치하는 패턴)이 캡처 되고 그룹 1이 캡처 됩니다.join'|'aaab

Lesson : 둘러보기 내부를 캡처 할 수 있습니다. 빈 공간을 사용하여 가독성을 높일 수 있습니다.


3 단계 : 미리보기를 "루프"로 리팩토링

카운팅 메커니즘을 도입하기 전에 패턴을 한 가지 수정해야합니다. 현재, 미리보기는 +반복 "루프" 밖에 있습니다. 이것은 우리가 우리의 b+뒤에 오는 것이 있다고 단언하고 싶었 기 때문에 지금까지는 괜찮습니다 a+. 그러나 우리가 정말로 하고 싶은 a것은 우리가 "루프"내부에서 일치 하는 각각에 대해 b그것에 상응하는 것이 있다고 주장하는 것입니다.

지금은 계산 메커니즘에 대해 걱정하지 말고 다음과 같이 리팩토링을 수행하십시오.

  • 우선 팩터 a+(?: a )+(주는 (?:…)비 촬상 기)
  • 그런 다음이 비 캡처 그룹 내에서 예견을 이동합니다.
    • 이제를 "보기" a*전에 "건너 뛰기" 해야 b+하므로 그에 따라 패턴을 수정해야합니다.

이제 우리는 다음을 가지고 있습니다.

$r3 = '/ ^ (?: a (?= a* (b+) ) )+ /x';
#                     └──┘  
#                       1   
#               └───────────┘ 
#                 lookahead   
#          └───────────────────┘
#           non-capturing group

출력은 이전과 동일하므로 ( ideone.com에서 볼 수 있음 ) 이와 관련하여 변경 사항이 없습니다. 중요한 것은 이제 우리는 "루프" 의 모든 반복 에서 주장을하고 있다는 것입니다 +. 현재 패턴에서는 이것이 필요하지 않지만 다음으로 자기 참조를 사용하여 그룹 1을 "수"로 만들 것입니다.

Lesson : 비 캡처 그룹 내에서 캡쳐 할 수 있습니다. 둘러보기를 반복 할 수 있습니다.


4 단계 : 계산을 시작하는 단계입니다.

다음과 같이 할 것입니다. 그룹 1을 다음과 같이 다시 작성합니다.

  • 의 첫 번째 반복이 끝날 +때 첫 번째 항목 a이 일치하면 캡처해야합니다.b
  • 두 번째 반복이 끝날 때 다른 반복 a이 일치하면bb
  • 세 번째 반복이 끝나면 다음을 캡처해야합니다. bbb
  • ...
  • n 번째 반복 이 끝나면 그룹 1은 b n을 캡처해야합니다.
  • b그룹 1로 캡처 하기에 충분하지 않으면 어설 션이 실패합니다.

지금은 그룹 1, 그래서 (b+), 같은를 다시 작성해야합니다 (\1 b). 즉 b, 이전 반복에서 캡처 한 그룹 1에 a를 "추가"하려고합니다 .

여기에는이 패턴에 "기본 케이스"가 누락되어 있다는 점에서 약간의 문제가 있습니다. 즉, 자체 참조없이 일치 할 수있는 경우입니다. 그룹 1이 "초기화되지 않음"을 시작하기 때문에 기본 케이스가 필요합니다. 아직 아무것도 캡처하지 않았으므로 (빈 문자열조차도) 자체 참조 시도는 항상 실패합니다.

이이 주위에 많은 방법이 있지만, 지금의하자에 대한 바로 자기 참조 일치 할 옵션을\1?. 이것은 완벽하게 작동 할 수도 있고 그렇지 않을 수도 있지만 그게 뭔지 보자. 문제가 있으면 그 다리를 건너 갈 것이다. 또한, 우리가 진행하는 동안 더 많은 테스트 케이스를 추가 할 것입니다.

$tests = array(
  'aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb', 'aabb', 'aaabbbbb', 'aaaaabbb'
);
 
$r4 = '/ ^ (?: a (?= a* (\1? b) ) )+ /x';
#                     └─────┘ | 
#                        1    | 
#               └──────────────┘ 
#                   lookahead    
#          └──────────────────────┘
#             non-capturing group

이제 출력은 다음과 같습니다 ( ideone.com에 표시됨 ).

aaa 0
aaab 1 aaa|b        # (*gasp!*)
aaaxb 0
xaaab 0
b 0
abbb 1 a|b          # yes!
aabb 1 aa|bb        # YES!!
aaabbbbb 1 aaa|bbb  # YESS!!!
aaaaabbb 1 aaaaa|bb # NOOOOOoooooo....

아하! 이제 솔루션에 정말 가까워진 것 같습니다! 우리는 자기 참조를 사용하여 그룹 1을 "계수"하도록 관리했습니다! 하지만 잠깐 ... 두 번째 및 마지막 테스트 케이스에 문제가 있습니다 !! bs 가 충분하지 않으며 어떻게 든 잘못 계산했습니다! 다음 단계에서 왜 이런 일이 발생했는지 살펴 보겠습니다.

Lesson : 자체 참조 그룹을 "초기화"하는 한 가지 방법은 자체 참조 일치를 선택 사항으로 만드는 것입니다.


4½ 단계 : 무엇이 잘못되었는지 이해

문제는 우리가 자기 참조 매칭을 선택적으로 만들었 기 때문에 "카운터"가 충분하지 않을 때 "재설정"할 수 있다는 것 b입니다. aaaaabbb입력으로 패턴을 반복 할 때마다 어떤 일이 발생하는지 자세히 살펴 보겠습니다 .

 a a a a a b b b

# Initial state: Group 1 is "uninitialized".
           _
 a a a a a b b b
  
  # 1st iteration: Group 1 couldn't match \1 since it was "uninitialized",
  #                  so it matched and captured just b
           ___
 a a a a a b b b
    
    # 2nd iteration: Group 1 matched \1b and captured bb
           _____
 a a a a a b b b
      
      # 3rd iteration: Group 1 matched \1b and captured bbb
           _
 a a a a a b b b
        
        # 4th iteration: Group 1 could still match \1, but not \1b,
        #  (!!!)           so it matched and captured just b
           ___
 a a a a a b b b
          
          # 5th iteration: Group 1 matched \1b and captured bb
          #
          # No more a, + "loop" terminates

아하! 4 번째 반복에서는 여전히 일치 할 수 있지만 일치 \1할 수 없습니다 \1b! 자체 참조 일치를에서 선택 사항으로 허용하기 때문에 \1?엔진은 역 추적하고 "아니요"옵션을 선택하여 일치하고 캡처 할 수 있습니다 b.

그러나 첫 번째 반복을 제외하고는 항상 자체 참조 만 일치시킬 수 \1있습니다. 우리가 우리의 이전 반복에서 캡처 한 것, 그리고 우리의 설정에서 우리는 항상 우리가 캡처 된 경우 (예 : 다시 일치 할 수 있기 때문에 이것은 물론, 분명 bbb지난 시간, 우리는 여전히있을 것입니다 보장하고 bbb있지만,이 수도 있고 bbbb이번이 아닐 수도 있습니다 ).

교훈 : 역 추적을 조심하세요. 정규식 엔진은 주어진 패턴이 일치 할 때까지 허용하는만큼 역 추적을 수행합니다. 이는 성능 (예 : 치명적인 역 추적 ) 및 / 또는 정확성에영향을 미칠 수 있습니다.


5 단계 : 구조에 대한 자기 소유!

"수정"은 이제 명확해야합니다. 선택적 반복과 소유 한정자를 결합 합니다. 즉, 단순히 ?를 사용하는 ?+대신 대신 사용하십시오 (소유격으로 정량화 된 반복은 그러한 "협력"이 전체 패턴의 일치를 초래할 수 있더라도 역 추적하지 않음을 기억하십시오).

매우 비공식적 인 용어로 이것은 무엇 ?+이며 다음 ???같이 말합니다.

?+

  • (선택 사항) "그럴 필요는 없습니다."
    • (소유 적) "하지만 거기 있으면 가져 가야하고 놓지 말아요!"

?

  • (선택 사항) "그럴 필요는 없습니다."
    • (욕심쟁이) "하지만 만약 그렇다면 지금 당장 받아도 돼"
      • (역 추적) "하지만 나중에 놓아 달라는 요청을받을 수도 있습니다!"

??

  • (선택 사항) "그럴 필요는 없습니다."
    • (마지 못해) "그리고 당신이 아직 그것을 받아 들일 필요는 없습니다."
      • (역 추적) "하지만 나중에 가져와야 할 수도 있습니다!"

우리의 설정에서, \1처음에는 거기에 있지 않을 것이지만, 그 이후에는 항상 거기에있을 것이고 , 우리는 항상 그것을 일치시키고 싶습니다. 따라서 \1?+우리가 원하는 것을 정확하게 달성 할 수 있습니다.

$r5 = '/ ^ (?: a (?= a* (\1?+ b) ) )+ /x';
#                     └──────┘  
#                         1     
#               └───────────────┘ 
#                   lookahead     
#          └───────────────────────┘
#             non-capturing group

이제 출력은 다음과 같습니다 ( ideone.com에서 볼 수 있음 ).

aaa 0
aaab 1 a|b          # Yay! Fixed!
aaaxb 0
xaaab 0
b 0
abbb 1 a|b
aabb 1 aa|bb
aaabbbbb 1 aaa|bbb
aaaaabbb 1 aaa|bbb  # Hurrahh!!!

Voilà !!! 문제 해결됨!!! 우리는 이제 정확히 우리가 원하는 방식으로 정확하게 계산하고 있습니다!

Lesson : 욕심, 주저, 소유 반복의 차이를 배웁니다. 선택적 소유는 강력한 조합이 될 수 있습니다.


6 단계 : 터치 마무리

그래서 지금 우리가 가지고있는 것은 a반복적으로 일치하는 패턴입니다. 일치하는 모든 패턴에 대해 그룹 1에서 캡처 된 a해당 패턴 이 있습니다. 더 이상없는 경우 또는 해당 대상 이 없어서 주장이 실패하면 종료됩니다. .b+aba

작업을 완료하려면 패턴에 추가하기 만하면 \1 $됩니다. 이것은 이제 어떤 그룹 1이 일치하는지에 대한 역 참조이며 그 뒤에 라인 앵커의 끝이 이어집니다. 앵커는 b문자열에 여분 의 가 없도록 합니다. 즉, 사실 우리는 a n b n을 가지고 있습니다.

다음은 10,000 자 길이를 포함하여 추가 테스트 케이스가있는 최종 패턴입니다.

$tests = array(
  'aaa', 'aaab', 'aaaxb', 'xaaab', 'b', 'abbb', 'aabb', 'aaabbbbb', 'aaaaabbb',
  '', 'ab', 'abb', 'aab', 'aaaabb', 'aaabbb', 'bbbaaa', 'ababab', 'abc',
  str_repeat('a', 5000).str_repeat('b', 5000)
);
 
$r6 = '/ ^ (?: a (?= a* (\1?+ b) ) )+ \1 $ /x';
#                     └──────┘  
#                         1     
#               └───────────────┘ 
#                   lookahead     
#          └───────────────────────┘
#             non-capturing group

:이 4 경기 발견 ab, aabb, aaabbb, 그리고 5000 B 5000 . 그것은 걸립니다 만 ideone.com에서 실행 0.06s .


7 단계 : 자바 테스트

따라서 패턴은 PHP에서 작동하지만 궁극적 인 목표는 Java에서 작동하는 패턴을 작성하는 것입니다.

public static void main(String[] args) {
 
        String aNbN = "(?x) (?:  a  (?= a* (\\1?+ b))  )+ \\1";
        String[] tests = {
                "",      // false
                "ab",    // true
                "abb",   // false
                "aab",   // false
                "aabb",  // true
                "abab",  // false
                "abc",   // false
                repeat('a', 5000) + repeat('b', 4999), // false
                repeat('a', 5000) + repeat('b', 5000), // true
                repeat('a', 5000) + repeat('b', 5001), // false
        };
        for (String test : tests) {
                System.out.printf("[%s]%n  %s%n%n", test, test.matches(aNbN));
        }
 
}
 
static String repeat(char ch, int n) {
        return new String(new char[n]).replace('\0', ch);
}

패턴이 예상대로 작동합니다 ( ideone.com에 표시됨 ).


그리고 이제 우리는 결론에 도달합니다 ...

a*룩어 헤드와 실제로 "주 +루프"는 둘 다 역 추적을 허용 한다고 말할 필요가 있습니다 . 독자들은 이것이 정확성 측면에서 왜 문제가되지 않는지, 왜 동시에 소유욕으로 만드는 것이 효과가 있는지 확인하도록 권장됩니다 (동일한 패턴에서 필수 소유물과 비 필수 소유물 수량자를 혼합하면 오해로 이어질 수 있음).

또한 n b n 과 일치 하는 정규식 패턴이 있다는 것이 깔끔하지만 실제로 이것이 항상 "최상의"솔루션은 아닙니다. 훨씬 더 나은 솔루션은 단순히을 일치 ^(a+)(b+)$시킨 다음 호스팅 프로그래밍 언어에서 그룹 1과 2가 캡처 한 문자열의 길이를 비교하는 것입니다.

PHP에서는 다음과 같이 보일 수 있습니다 (ideone.com에서 볼 수 있음 ).

function is_anbn($s) {
   return (preg_match('/^(a+)(b+)$/', $s, $groups)) &&
      (strlen($groups[1]) == strlen($groups[2]));
}

이 기사의 목적은 정규식이 거의 모든 것을 할 수 있다는 것을 독자들에게 설득하는 것이 아닙니다 . 그것은 분명히 할 수 없으며, 할 수있는 일이라 할지라도, 더 간단한 해결책으로 이끄는 경우 호스팅 언어에 대한 최소한 부분적인 위임을 고려해야합니다.

위에서 언급했듯이이 기사는 반드시 [regex]stackoverflow 태그가 지정 되어 있지만 아마도 그 이상일 것입니다. 주장, 중첩 된 참조, 소유 적 한정사 등에 대해 배우는 데는 확실히 가치가 있지만, 여기서 더 큰 교훈은 문제를 해결하려고 시도 할 수있는 창의적인 프로세스, 당신이 겪을 때 종종 요구하는 결단력과 노력입니다. 다양한 제약, 다양한 부품에서 체계적인 구성으로 작업 솔루션 구축 등


보너스 자료! PCRE 재귀 패턴!

PHP를 가져 왔기 때문에 PCRE가 재귀 패턴과 서브 루틴을 지원한다고 말할 필요가 있습니다. 따라서 다음 패턴이 작동합니다 preg_match( ideone.com에서 볼 수 있음 ).

$rRecursive = '/ ^ (a (?1)? b) $ /x';

현재 Java의 정규식은 재귀 패턴을 지원하지 않습니다.


더 많은 보너스 소재! 일치 하는 a n b n c n !!

그래서 우리 는 비정규 적이지만 여전히 컨텍스트가없는 n b n 을 일치 시키는 방법을 보았습니다 .하지만 컨텍스트가없는 것도 아닌 n b n c n을 일치시킬 수도 있습니까?

물론 대답은 예입니다! 독자는이 문제를 스스로 해결하도록 권장하지만 솔루션은 아래에 제공됩니다 ( ideone.com의 Java 구현 포함 ).

^ (?: a (?= a* (\1?+ b) b* (\2?+ c) ) )+ \1 \2 $


이 긴 답변에 실수 / 오타가있을 수 있으므로 의견을 남겨 주시면 제가 직접 수정할 수 있습니다.
polygenelubricants

잘 했어. 읽는 데 시간이 좀 걸리 겠지만 맨 마지막 줄은 기본적으로 읽을 수 없습니다. 너무 작은 글꼴입니다. ------ 아 잠깐. 그거 feature? .... 확실하지가 좋은 아이디어 인 경우. 마지막 기호가 무엇인지 알고 있지만 읽을 수 없습니다 (복사 붙여 넣기 제외).
Peter Ajtai

6
@Peter : 작은 텍스트를 강조 표시하고 복사하여 다른 곳에 붙여 넣습니다. 의도적으로 읽기가 어렵습니다. 보너스 퍼즐에 대한 해결책 인 스포일러입니다.
polygenelubricants

8
+1 : 환상적인 설명,이 "고급 기사"는 훌륭한 아이디어입니다.
Callum Rogers

1
@LarsH PHP preg_match()PCRE 의 예입니다 . Java 정규식 은 이전 버전의 Perl 정규식을 기반으로하는 것 같습니다 . 즉, PHP 정규식이 Java 버전보다 강력합니다. 현재 2013년 2월 21일 , pcre.txt은 그것이한다고 펄 5.12로 약 해당한다 . Perl은 현재 5.16이며, 5.18은 몇 달 동안 쉬었습니다. (실제로 그 당시 정규식에 많이 추가되지 않았습니다)
Brad Gilbert

20

재귀 패턴을 지원하는 PCRE에 대한 언급이 없기 때문에 문제의 언어를 설명하는 가장 간단하고 효율적인 PCRE 예제를 지적하고 싶습니다.

/^(a(?1)?b)$/

+1 와우, PCRE가 재귀 패턴을 지원하는지 몰랐습니다 (아직 배우는 중입니다! 매일!). 이 정보를 수용하기 위해 기사를 수정했습니다. 그래도 재귀 패턴이 일치한다고 생각하지 않습니다 a^n b^n c^n.
polygenelubricants

이 옵션은 더 간단하지만 게시 된 답변만큼 좋지는 않습니다. 재귀는 긴 문자열에서 오버플로됩니다.
Kobi

@Kobi 이것은 "좋은"의 정의에 따라 다릅니다. 예를 들어 재귀 솔루션은 다른 솔루션 ( codepad.viper-7.com/CWgy7c ) 보다 약 10 배 더 빠릅니다 . 그리고 이해하기 훨씬 쉽습니다. 재귀 적 솔루션은 문법을 정규식으로 직접 변환하는 것입니다 (실제로 문법화 된 형식으로 작성하면 작동합니다).
NikiC 2011 년

1
@polygeniclubricants를 사용하면 해당 패턴을 두 개의 재귀 패턴과 일치시킬 수 있습니다. 하나 는 캡처하지 않고 as와 bs 를 소비하는 것입니다 (그리고 재귀에 동일한 양이 있는지 확인). 그런 다음 모든 a를 탐욕스럽게 소비하는 캡처 링 정규식이 뒤 따르고 재귀를 적용합니다. 패턴을 사용하고 동일한 수의 bs 및 cs 가 있는지 확인합니다 . 정규식은 /^(?=(a(?-1)?b)c)a+(b(?-1)?c)$/x. 출처
Josh Reback

11

질문에서 언급했듯이-.NET 밸런싱 그룹을 사용하면 a n b n c n d n … z n 유형의 패턴을 다음 과 같이 쉽게 일치시킬 수 있습니다.

^
  (?<A>a)+
  (?<B-A>b)+  (?(A)(?!))
  (?<C-B>c)+  (?(B)(?!))
  ...
  (?<Z-Y>z)+  (?(Y)(?!))
$

예 : http://www.ideone.com/usuOE


편집하다:

재귀 패턴을 사용하는 일반화 된 언어에 대한 PCRE 패턴도 있지만 예견이 필요합니다. 나는 이것이 위의 직접적인 번역이라고 생각하지 않습니다.

^
  (?=(a(?-1)?b))  a+
  (?=(b(?-1)?c))  b+
  ...
  (?=(x(?-1)?y))  x+
     (y(?-1)?z)
$

예 : http://www.ideone.com/9gUwF


1
@poly : 감사합니다 :). 사실 저는 .NET 패턴에 익숙하지 않지만 이런 종류의 패턴의 경우 균형 그룹이 매우 쉬운 것으로 판명되었으므로이 답변을 보완합니다.
kennytm

재귀 패턴으로 이것을 할 수 있습니까? 할 수 없다면 밸런싱 그룹이 재귀 패턴이 할 수없는 일을 할 수 있다는 흥미로운 트위스트입니다. (예, 보충제에 대단히 감사합니다).
polygenelubricants

그런데 .NET 솔루션을 생략 한 이유는 "어떻게 a^n b^n.NET 정규식 과 일치시킬 수 있습니까?"라는 계획이 있기 때문입니다. 기사를 작성 하겠지만 원하는 경우 작성하는 것을 환영합니다. 나는이 기사를 나 자신을 위해서만하는 것이 아닙니다. 나는 다른 사람들도 사이트에 좋은 콘텐츠를 갖도록 격려하고 싶습니다.
polygenelubricants

재귀 패턴으로 수행하는 방법을 찾으면 업데이트하십시오. 나는 밸런싱 그룹을 가지고 놀면서 길이가 피보나치 시리즈를 만드는 단어를 포착했지만 작동하지 못했습니다. 내가 한 것과 유사한 둘러보기를 사용하는 것이 가능할 수 있습니다.
Kobi

1
이 패턴의 PCRE 버전은 다음 문자 청크가 이전 문자보다 길면 일치하므로 약간 결함이 있음을 지적하고 싶습니다. 여기를 참조하십시오 : regex101.com/r/sdlRTm/1 당신의 필요 추가 (?!b), (?!c)그래서 같은 캡처 그룹 후 등 : regex101.com/r/sdlRTm/2
jaytea
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.