[regex] 탐욕 vs. 꺼리는 대 소유 정량화

나는 정규 표현식에 대한 이 훌륭한 튜토리얼 을 발견 했으며 “욕심 많음”, “마지 못함”및 “포지티브”정량자가하는 일을 직관적으로 이해하지만 이해에 심각한 구멍이있는 것 같습니다.

특히 다음 예제에서

Enter your regex: .*foo  // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.

설명은 언급 먹고 , 문자가되어 전체 입력 문자열을 소비 정규, 백 오프 , “foo는”의 제일 오른쪽으로 출현이되어 역류 등,

불행히도, 좋은 은유에도 불구하고, 나는 아직도 누가 무엇을 먹었는지 이해하지 못한다 … 정규식 엔진이 어떻게 작동 하는지 설명하는 다른 튜토리얼을 알고 있습니까?

또는 다음 단락에서 다소 다른 문구로 설명 할 수 있다면 크게 감사하겠습니다.

첫 번째 예는 욕심 많은 수량 자. *를 사용하여 0 번 이상 “anything”을 찾은 다음 문자 “f” “o” “o”를 찾습니다. 수량자가 욕심 많기 때문에 식의. * 부분은 먼저 전체 입력 문자열을 사용합니다. 이 시점에서 마지막 세 글자 ( “f” “o” “o”)가 이미 사용 되었기 때문에 전체 표현은 성공할 수 없습니다 ( 누군가? ). 따라서 매처 는 “foo”의 가장 오른쪽 어커런스가 역류 될 때까지 (오른쪽에서 왼쪽으로? ) 한 문자 씩 천천히 백 오프합니다 ( 이것은 무엇을 의미합니까? ).

그러나 두 번째 예는 꺼려하기 때문에 먼저 ” 누가 “를 “아무것도” 소비 하지 않는 것으로 시작합니다 . “foo는”이 문자열의 시작 부분에 표시되지 않기 때문에, (제비 강제거야 누구 ? 제비) 0 4. 우리의 테스트 장치에서 첫 경기를 트리거 첫 글자 (에 “X”)를 프로세스를 계속 입력 문자열이 소진 될 때까지. 4와 13에서 다른 경기를 찾습니다.

수량자가 소유하기 때문에 세 번째 예에서 일치하는 항목을 찾지 못했습니다. 이 경우 전체 입력 문자열은. * +에 의해 소비됩니다 ( 어떻게? ). 식 끝에 “foo”를 만족시키기 위해 아무것도 남겨 두지 않습니다. 역행하지 않고 모든 것을 빼앗고 싶을 때 소유격 한정자를 사용하십시오 (역행의 의미는 무엇입니까? ). 일치하는 항목을 즉시 찾을 수없는 경우 동등한 욕심 수량자를 능가합니다.



답변

나는 그것을 줄 것이다.

욕심 많은 가능한 한 정량 첫 번째 경기. 따라서 .*전체 문자열과 일치합니다. 그런 다음 매처는 f다음 을 일치 시키려고 하지만 문자가 남아 있지 않습니다. 따라서 “백 트랙 (backtracks)”으로 욕심 많은 수량자를보다 적은 문자와 일치시킵니다 (문자열의 끝에 “o”는 일치하지 않습니다). 그래도 f정규 표현식의 와 일치하지 않으므로 한 단계 더 뒤로 이동하여 욕심 많은 수량자가 문자를 다시 한 번 덜 일치시킵니다 (문자열의 끝에 “oo” 가 일치하지 않음 ). 그것은 여전히f 정규 표현식의 와 일치하지 않으므로 한 단계 더 뒤로 추적합니다 (문자열 끝에 “foo” 가 일치하지 않음 ). 이제 정규 f표현식에서 정규 표현식 과 일치합니다 . 와도 일치합니다. 성공!oo

꺼려 가능한 한 적게는 “비 욕심”정량 첫 경기 나. 따라서 .*처음에는 아무것도 일치하지 않으므로 전체 문자열 이 일치하지 않습니다. 그런 다음 매처는 f다음 을 일치 시키려고 하지만 문자열의 일치 하지 않는 부분은 “x”로 시작하므로 작동하지 않습니다. 따라서 매처 백 트랙은 탐욕스럽지 않은 수량자를 하나 이상의 문자와 일치시킵니다 (이제 “x”와 일치하고 “fooxxxxxxfoo”는 일치하지 않습니다). 그런 다음 f성공하는 을 일치 시키려고 시도 하고 정규 표현식 의 o및 다음 o도 일치합니다. 성공!

그런 다음 동일한 프로세스에 따라 문자열 “xxxxxxfoo”의 일치하지 않는 나머지 부분으로 프로세스를 다시 시작합니다.

소유 정량 그냥 욕심 정량처럼이지만, 역 추적하지 않습니다. 따라서 .*전체 문자열 을 일치시키는 것으로 시작하여 일치하는 것은 없습니다. 그런 다음 f정규 표현식에서 일치하는 항목이 없습니다 . 소유 수량 화기가 역 추적하지 않기 때문에 경기가 실패합니다.


답변

장면을 시각화하는 것은 내 연습 결과입니다.

시각적 이미지


답변

나는 이전에 ‘역류하다’또는 ‘후퇴하다’라는 정확한 용어를들은 적이 없다. 이를 대체하는 문구는 “역 추적 (backtracking)”이지만 “역류 (regurgitate)”는 “역 추적 (backtracking) 전에 잠정적으로 받아 들여진 내용이 다시 그것을 버렸다”는 것만 큼 좋은 문구처럼 보인다.

중요한 것은 대부분의 정규식 엔진에 대해들은 것을 실감하는 역 추적 : 그들은 것 잠정적으로 정규식의 전체 내용과 일치하는 동안, 잠재적 인, 부분 일치에 동의합니다. 첫 번째 시도에서 정규식을 완전히 일치시킬 수 없으면 정규식 엔진이 일치하는 항목 중 하나를 역 추적 합니다. 그것은 일치하려고합니다 *, +, ?, 교대, 또는 {n,m}다른 반복을 다시 시도하십시오. (그렇습니다.이 과정 시간이 오래 걸릴 있습니다.)

첫 번째 예는 욕심 많은 수량 자. *를 사용하여 0 번 이상 “anything”을 찾은 다음 문자 “f” “o” “o”를 찾습니다. 수량자가 욕심 많기 때문에 식의. * 부분은 먼저 전체 입력 문자열을 사용합니다. 이 시점에서 마지막 세 글자 ( “f” “o” “o”)가 이미 사용 되었기 때문에 전체 표현은 성공할 수 없습니다 ( 누군가? ).

마지막 세 글자는, f, o, 및 o이미 초기에 의해 소비 된 .*규칙의 부분. 그러나 정규식의 다음 요소 인 f입력 문자열에는 아무것도 남아 있지 않습니다. 엔진은 초기 경기에서 역 추적 하고 .*마지막 문자까지 일치시킵니다. ( 세 개의 문자 그대로의 용어가 있기 때문에이 단계에서 구현 세부 사항을 알지 못하기 때문에 현명 하고 마지막 3 개로 거슬러 올라갈 수 있습니다 .)

따라서 매처 는 가장 오른쪽에있는 “foo”가 역류 될 때까지 (오른쪽에서 왼쪽으로? ) 한 번에 한 문자 씩 천천히 백 오프합니다 ( 이것은 무엇을 의미합니까? ).

(가)이 수단 foo했다 잠정적 때 매칭을 포함하고 .*. 해당 시도가 실패했기 때문에 정규식 엔진은에서 문자를 하나 더 적게받습니다 .*. 성공적인 일치 있었다면 전에.* 이 예에서는 다음 엔진은 아마도 단축하려고 할 것입니다 .*일치 (에서 오른쪽에서 왼쪽 당신이 지적한대로 욕심 규정이기 때문에,)가 일치 할 수 없습니다, 그리고 경우 전체 입력, 내 가상의 예에서 이전 에 일치했던 것을 다시 평가해야 할 수도 있습니다 .*.

경기가 성공하고 검색이 종료됩니다.

그러나 두 번째 예는 꺼려하기 때문에 먼저 ” 누가 “를 “아무것도” 소비 하지 않는 것으로 시작합니다 . “foo”이기 때문에

는 초기에 아무것도 소비하지 않으며 .?*나머지 정규 표현식과 일치하는 가장 짧은 양을 소비합니다.

끈의 시작 부분에 나타나지 않는, 그것은 삼키는 것입니다 ( 누가 삼키는가?)

다시는 .?*최단 일치와 전체 정규식과 일치하는 초기 실패에 되돌아 후, 첫 번째 문자를 소비한다. (이 경우 정규식 엔진 은 마지 .*?못하기 때문에 왼쪽에서 오른쪽으로 일치를 확장합니다 .*?.)

첫 번째 문자 ( “x”)는 0과 4에서 첫 번째 일치를 트리거합니다. 테스트 하네스는 입력 문자열이 소진 될 때까지 프로세스를 계속합니다. 4와 13에서 다른 경기를 찾습니다.

수량자가 소유하기 때문에 세 번째 예는 일치하는 항목을 찾지 못합니다. 이 경우 전체 입력 문자열은. * +, ( how? )로 소비됩니다.

A .*+는 가능한 한 많이 소비하며 정규 표현식 전체가 일치하는 항목을 찾지 못하면 새로운 일치 항목을 찾기 위해 역 추적하지 않습니다 . 소유 형식은 역 추적을 수행하지 않기 때문에 아마도을 사용하는 .*+것이 아니라 문자 클래스 또는 이와 유사한 제한을 사용하는 것을 많이 볼 것입니다 account: [[:digit:]]*+ phone: [[:digit:]]*+.

입력이 일치하지 않으면 잠재적 일치 항목을 역 추적해서는 안된다고 정규 표현식 엔진에 알려주기 때문에 정규 표현식 일치 속도를 크게 높일 수 있습니다. (일치하는 모든 코드를 직접 작성 putc(3)해야하는 경우 입력 문자를 ‘푸시 백’하는 데 사용하지 않는 것과 비슷합니다. 첫 번째 시도에서 작성할 수있는 순진한 코드와 매우 유사합니다. 정규식 엔진은 제외합니다. 푸시 백의 단일 문자보다 더 나은 방법으로, 그들은 모두 다시 0으로 되 감고 다시 시도 할 수 있습니다. 🙂

그러나 잠재적 인 속도 향상보다 더 정확하게 일치 해야하는 정규식을 작성할 수 있습니다. 나는 쉬운 예를 제시하는 데 어려움을 겪고 있지만 소유 vs 욕심 많은 수량자를 사용하여 정규 표현식을 작성하면 다른 결과를 얻을 수 있으며 하나 또는 다른 것이 더 적합 할 수 있습니다.

표현의 끝에서 “foo”를 만족시키기 위해 아무것도 남겨 두지 않았습니다. 역행하지 않고 모든 것을 빼앗고 싶을 때 소유격 한정자를 사용하십시오 (역행의 의미는 무엇입니까? ). 그것은 능가 할 것이다

이 문맥에서 “백 오프”는 “백 트래킹”을 의미합니다. 잠정적 인 부분 일치를 버려 다른 부분 일치를 시도하여 성공하거나 실패 할 수 있습니다.

일치하는 것을 즉시 찾을 수없는 경우 동등한 욕심 수량 자


답변

http://swtch.com/~rsc/regexp/regexp1.html

나는 그것이 인터넷상에서 가장 좋은 설명인지는 확실하지 않지만, 합리적으로 잘 작성되고 적절하게 상세하게 설명되어 있으며, 계속해서 되돌아 가고 있습니다. 확인하고 싶을 수도 있습니다.

보고있는 것과 같은 간단한 정규식에 대해 더 높은 수준 (자세한 설명은 필요 없음)을 원하면 정규식 엔진이 역 추적을 통해 작동합니다. 기본적으로 문자열의 한 섹션을 선택 ( “eat”)하고 정규식을 해당 섹션과 일치 시키려고합니다. 일치하면 좋습니다. 그렇지 않은 경우 엔진은 문자열 섹션의 선택을 변경하고 가능한 모든 선택을 시도 할 때까지 정규 표현식과 해당 섹션 등을 일치 시키려고 시도합니다.

이 프로세스는 재귀 적으로 사용됩니다. 주어진 정규 표현식과 문자열을 일치 시키려고 시도 할 때 엔진은 정규 표현식을 조각으로 나누고 알고리즘을 각 조각에 개별적으로 적용합니다.

욕심, 마지 못해 및 소유 수량화의 차이점은 엔진이 일치시킬 문자열 부분을 선택하고 처음 작동하지 않는 경우 해당 선택을 수정하는 방법을 선택할 때 시작됩니다. 규칙은 다음과 같습니다.

  • 탐욕스러운 수량자는 엔진이 전체 문자열 (또는 적어도 정규식의 이전 부분과 아직 일치하지 않은 모든 문자열) 로 시작 하고 정규 표현식과 일치하는지 확인 하도록 엔진에 지시합니다 . 그렇다면 훌륭합니다. 엔진은 나머지 정규 표현식으로 계속 진행할 수 있습니다. 그렇지 않으면 다시 시도하지만 확인할 문자열 섹션에서 한 문자 (마지막 문자)를 자릅니다. 그래도 작동하지 않으면 다른 캐릭터 등을 제거합니다. 따라서 탐욕스러운 수량자는 가능한 일치 항목을 가장 긴 순서에서 가장 짧은 순서로 확인합니다.

  • 꺼리는 정량자는 엔진이 가능한 가장 짧은 문자열로 시작하도록 지시합니다. 일치하면 엔진을 계속 사용할 수 있습니다. 그렇지 않은 경우 검사중인 문자열의 섹션에 한 문자를 추가 하여 시도하고 일치하는 항목을 찾거나 전체 문자열이 다 사용 된 때까지 계속 시도합니다. 따라서 꺼리는 수량자는 가장 짧은 것부터 가장 긴 것까지 가능한 일치를 검사합니다.

  • 소유 수량자는 첫 번째 시도에서 탐욕스러운 수량 자와 같습니다. 전체 문자열을 확인하여 엔진을 시작하도록 지시합니다. 차이점은 그것이 작동하지 않으면 소유격 수량자는 일치가 바로 실패했다고보고한다는 것입니다. 엔진은보고있는 문자열의 섹션을 변경하지 않으며 더 이상 시도하지 않습니다.

그렇기 때문에 소유 .*+문자열 한정자가 일치하지 않는 이유는 다음과 같습니다. 전체 문자열과 일치하는 문자열을 검사하지만 엔진은 foo그 이후 에 추가 문자를 찾습니다. 하지만 물론 찾지 못합니다. 이미 문자열의 끝에 있습니다. 욕심 많은 수량 .*자인 경우 역 추적하고 마지막 문자부터 마지막 ​​문자까지, 세 번째 문자에서 마지막 문자까지, 그리고 네 번째 문자에서 마지막 문자까지만 일치 시키려고 시도 합니다. 가 foo애프터 왼쪽은 .*문자열의 이전 부분을 “먹어”하고있다.


답변

여기에 셀 및 인덱스 위치를 사용합니다 (셀을 인덱스와 구별 하려면 여기다이어그램 참조 ).

욕심-욕심 많은 수량 자 및 전체 정규식에 가능한 한 많이 일치하십시오. 일치하는 것이 없으면 욕심 많은 수량 자에 역 추적하십시오.

입력 문자열 : xfooxxxxxxfoo
정규식 : . * foo

정규식 에는
(i) ‘. *’및
(ii) ‘foo’

두 부분이 있습니다. 아래 각 단계에서 두 부분을 분석합니다. ‘Pass’또는 ‘Fail’과 일치하는 추가 설명은 중괄호 안에 설명되어 있습니다.

1 단계 :
(i). * = xfooxxxxxxfoo-PASS ( ‘. *’는 탐욕스러운 수량 자이며 전체 입력 문자열을 사용합니다.)
(ii) foo = 인덱스 13 이후에 일치하는 문자가 없습니다.-실패
일치.

2 단계 :
(i). * = xfooxxxxxxfo-PASS (욕심쟁이 수량 자 ‘. *’의 역 추적)
(ii) foo = o-FAIL
일치하지 못했습니다.

3 단계 :
(i). * = xfooxxxxxxf-PASS (욕심쟁이 정량 자 ‘. *’의 역 추적)
(ii) foo = oo-FAIL
일치하지 못했습니다.

4 단계 :
(i). * = xfooxxxxxx-PASS (욕심 많은 수량 자 ‘. *’에서 역 추적)
(ii) foo = foo-PASS
Report MATCH

결과 : 1 개의 일치 항목
인덱스 0에서 시작하여 인덱스 13에서 끝나는 “xfooxxxxxxfoo”텍스트를 찾았습니다.

마지 못해-마지 못해 정량 자와 최대한 일치하지 않고 전체 정규식과 일치시킵니다. 일치하지 않는 경우 꺼리는 수량 자에 문자를 추가하십시오.

입력 문자열 : xfooxxxxxxfoo
정규식 : . *? foo

위 정규식에는 두 부분이 있습니다.
(i) ‘. *?’ 그리고
(ii) ‘foo’

1 단계 :
. *? = ”(공백)
-PASS (마지막 정량화 ‘. *?’에 가능한 한 적은 일치합니다. ”가있는 인덱스 0이 일치합니다.) foo = xfo-FAIL (셀 0,1,2-즉 사이의 인덱스 0과 3)
일치하지 못했습니다.

2 단계 :
. *? = x-PASS (마지막 정량 기 ‘. *?’에 문자 추가. ‘x’가있는 셀 0이 일치합니다.)
foo = foo-PASS
Report MATCH

3 단계 :
. *? = ”(공백)-PASS (마지막 정량 기 ‘. *?’와 가능한 한 적게 일치합니다. ”가있는 인덱스 4는 일치합니다.)
foo = xxx-FAIL (셀 4,5,6-즉 사이의 인덱스 4 및 7)
일치하지 못했습니다.

4 단계 :
. *? = x-PASS (마지
못한 수량 자 ‘. *?’에 문자 추가. 셀 4).
foo = xxx-FAIL (셀 5,6,7-즉 5와 8 사이의 색인)
일치하지 못했습니다.

5 단계 :
. *? = xx-PASS (마지
못한 정량 자 ‘. *?’에 문자 추가. 셀 4 ~ 5)
foo = xxx-FAIL (셀 6,7,8-6과 9 사이의 색인)
일치하지 못했습니다.

6 단계 :
. *? = xxx-PASS (마지
못한 정량 자 ‘. *?’에 문자 추가. 셀 4 ~ 6)
foo = xxx-FAIL (셀 7,8,9-7과 10 사이의 색인)
일치하지 못했습니다.

7 단계 :
. *? = xxxx-PASS (마지
못한 정량 자 ‘. *?’에 문자 추가. 셀 4 ~ 7) foo = xxf-FAIL (셀 8,9,10-즉 8과 11 사이의 색인)
일치하지 못했습니다.

8 단계 :
. *? = xxxxx-PASS (마지
못한 정량 자 ‘. *?’에 문자 추가. 셀 4 ~ 8) foo = xfo-FAIL (셀 9,10,11-즉 9와 12 사이의 색인)
일치하지 못했습니다.

9 단계 :
. *? = xxxxxx-PASS (마지 못한 정량 자 ‘. *?’에 문자 추가. 셀 4 ~ 9)
foo = foo-PASS (셀 10,11,12-즉 10과 13 사이의 색인)
보고서 일치

10 단계 :
. *? – ‘(?.. *’지수 (13)가 비어 있습니다. = ‘일치 꺼린 정량 가능한 한 적게) PASS는 (빈)는’
푸 = 모든 문자가 일치하는 남아 있지 – FAIL을 (일치하는 인덱스 (13) 후 아무것도 없다)
일치 실패한.

결과 : 2 개의 일치 항목
인덱스 0에서
시작하여 인덱스 4에서 끝나는 “xfoo”텍스트를 찾았습니다. 인덱스 4에서 시작하여 인덱스 13에서 끝나는 “xxxxxxfoo”텍스트를 찾았습니다.

소유 성-소유량에 최대한 일치하고 정규식 전체를 일치시킵니다. 역 추적하지 마십시오.

입력 문자열 : xfooxxxxxxfoo
정규식 : . * + foo

위 정규식에는 ‘. * +’와 ‘foo’의 두 부분이 있습니다.

1 단계 :
. * + = xfooxxxxxxfoo-PASS (소유 수량화 ‘. *’와 최대한 일치)
foo = 일치하는 문자가 없습니다 -FAIL ( 색인 13 이후에 일치하는 문자가 없음)
일치하지 못했습니다.

참고 : 역 추적은 허용되지 않습니다.

결과 : 0 일치


답변

욕심 : “가장 긴 문자 시퀀스와 일치”

꺼리는 : “가장 짧은 문자 순서와 일치”

Possessive : (욕심스럽고 꺼리는 것과 대조적으로) 전체 정규 표현식과 일치하는 것을 찾지 않기 때문에 조금 이상합니다.

그건 그렇고 : 정규식 패턴 매처 구현은 역 추적을 사용하지 않습니다. 모든 실제 패턴 매처는 정규식의 복잡성과 거의 독립적으로 매우 빠릅니다!


답변

Greedy Quantification 에는 반복 중에 문자열의 나머지 확인되지 않은 문자를 모두 사용하여 패턴 일치가 포함됩니다. 유효하지 않은 문자는 활성 순서로 시작 합니다 . 일치하지 않을 때마다 끝에있는 문자가 검역 되고 검사가 다시 수행됩니다.

정규식 패턴의 선행 조건 만 활성 시퀀스에 의해 충족되면 검역소에 대해 나머지 조건을 검증하려고 시도합니다. 이 유효성 검사에 성공하면 검역소에서 일치하는 문자의 유효성이 검사되고 일치하지 않는 나머지 문자는 확인되지 않은 상태로 유지되며 다음 반복에서 프로세스가 새로 시작될 때 사용됩니다.

문자 흐름은 활성 시퀀스에서 검역소로 이동합니다. 결과적으로 원래 시퀀스의 많은 부분이 가능한 일치 항목에 포함됩니다.

마지 못한 정량화 는 문자의 흐름이 반대 인 점을 제외하고는 탐욕적 자격과 거의 동일합니다. 즉, 검역 에서 시작 하여 활성 시퀀스로 이동 합니다. 결과적으로 가능한 한 원본 시퀀스 중 적은 부분이 일치 항목에 포함됩니다.

소유 정량화 에는 검역소 가 없으며 고정 된 활성 순서로 모든 것이 포함 됩니다 .