.NET 정규식에서 "그룹"과 "캡처"의 차이점은 무엇입니까?


161

.NET의 정규 표현식 언어와 관련하여 "그룹"과 "캡처"의 차이점이 무엇인지 조금 모호합니다. 다음 C # 코드를 고려하십시오.

MatchCollection matches = Regex.Matches("{Q}", @"^\{([A-Z])\}$");

나는 이것이 문자 'Q'에 대한 단일 캡처를 초래할 것으로 기대하지만 반환 된 속성을 인쇄하면 MatchCollection다음을 볼 수 있습니다.

matches.Count: 1
matches[0].Value: {Q}
        matches[0].Captures.Count: 1
                matches[0].Captures[0].Value: {Q}
        matches[0].Groups.Count: 2
                matches[0].Groups[0].Value: {Q}
                matches[0].Groups[0].Captures.Count: 1
                        matches[0].Groups[0].Captures[0].Value: {Q}
                matches[0].Groups[1].Value: Q
                matches[0].Groups[1].Captures.Count: 1
                        matches[0].Groups[1].Captures[0].Value: Q

여기서 정확히 무슨 일이 일어나고 있습니까? 나는 전체 경기에 대한 캡처가 있다는 것을 이해하지만 그룹은 어떻게 들어 옵니까? 왜 matches[0].Captures문자 'Q'에 대한 캡처를 포함 하지 않습니까?

답변:


126

처음 퍼지 한 사람은 아닙니다. 다음은 유명한 Jeffrey Friedl의 의견입니다 (437 페이지 이상).

보기에 따라 일치하는 결과에 흥미로운 새로운 차원이 추가되거나 혼란과 팽창이 추가됩니다.

그리고 더 :

Group 개체와 Capture 개체의 주요 차이점은 각 Group 개체에는 일치하는 동안 그룹이 일치하는 모든 중간 일치 항목과 그룹과 일치하는 최종 텍스트를 나타내는 캡처 모음이 포함되어 있다는 것입니다 .

그리고 몇 페이지 후, 이것이 그의 결론입니다.

.NET 문서를 지나서 실제로 이러한 개체가 무엇을 추가하는지 이해 한 후에는 그에 대한 감정이 혼동되었습니다. 한편으로는 흥미로운 혁신 [..]이며 대부분의 경우에 사용되지 않는 기능의 효율성 부담 [..]을 추가하는 것으로 보입니다.

다시 말해서 : 그것들은 매우 유사하지만, 때때로 그리고 그것이 일어날 때, 그것들을 사용할 수 있습니다. 또 다른 회색 수염을 키우기 전에 캡처를 좋아할 수도 있습니다 ...


위의 내용이나 다른 게시물에서 언급 한 내용이 실제로 귀하의 질문에 대답하지 않는 것 같으므로 다음을 고려하십시오. 캡처는 일종의 기록 추적기로 생각하십시오. 정규 표현식이 일치하면 왼쪽에서 오른쪽으로 문자열을 통과하고 (순간 역 추적 무시) 일치하는 괄호를 발견하면 $x(x는 모든 숫자 임) 에 저장합니다 $1.

캡처 괄호를 반복 할 때 일반 정규식 엔진은 전류를 버리고 $1새 값으로 대체합니다. .NET이 아닙니다.이 기록을 유지하고에 저장합니다 Captures[0].

정규식을 다음과 같이 변경하면 :

MatchCollection matches = Regex.Matches("{Q}{R}{S}", @"(\{[A-Z]\})+");

첫 번째는 것을 알 Group한 것 Captures(첫 번째 그룹 항상 동일, 즉, 전체 일치되는 $0)과 두 번째 그룹이 개최 {S}즉 마지막 일치하는 그룹. 그러나 여기에 캐치가 있습니다. 다른 두 캐치를 찾으려면 및에 Captures대한 모든 중간 캡처가 포함 된에 있습니다 .{Q} {R}{S}

문자열에 명확하게있는 개별 캡처와의 마지막 일치 만 표시하는 다중 캡처에서 얻을 수있는 방법에 대해 궁금한 경우을 사용해야합니다 Captures.

최종 질문에 대한 마지막 단어 : 총 일치는 항상 총 캡처가 하나이므로 개별 그룹과 혼합하지 마십시오. 캡처는 그룹 내에서만 흥미 롭습니다 .


1
a functionality that won't be used in the majority of cases보트를 놓친 것 같아 단기적 (?:.*?(collection info)){4,20}으로 효율이 수백 퍼센트 이상 증가합니다.

1
@ sln, 당신이 무엇을 말하고 있는지, 누가 '그'인지 (friedl?) 모르겠습니다. 여러분이 제시 한 예는이 토론이나 사용 된 표현과 관련이없는 것 같습니다. 게다가, 욕심없는 정량자는 욕심있는 정량 자보다 매우 효율적이지 않으며 입력 세트에 대한 지식과 신중한 성능 테스트가 필요합니다.
Abel

@Abel-나는 이것의 복제로 표시된 질문에서 여기에 도착했습니다. Friedl이 인용 된 것을 봅니다. 이 게시물은 오래되었으며 최신 상태로 유지하려면 새로 고침해야합니다. Dot Net을 통해서만이 작업을 수행 할 수 있으며, 대부분의 다른 것과는 별개의 것입니다. 분류 : 정량화 된 비 캡처 전체 그룹 예 (?:..)+. .*?캡처 하위 표현식 (그룹)까지 모든 항목 을 느리게 일치 시킵니다. 계속하십시오. 한 번의 매치 내에서 그룹 모음은 필요한 것의 배열을 생성합니다. 다음에 찾을 필요가 없으며 재진입이 없어 10 ~ 20 배 이상 빠릅니다.

1
@ sln,이 질문은 다른 것에 관한 것이며 다른 정규식 엔진 (그룹 대 캡처, 제목 참조)에는없는 .net 기능에 관한 것입니다. 여기에 오래된 것이 보이지 않습니다. .net은 여전히 ​​동일하게 작동합니다. 실제로이 부분은 .net에서 오랫동안 변경되지 않았습니다. 성능은 문제의 일부가 아닙니다. 예, 비 캡처 그룹화가 빠르지 만 여기서도 주제는 반대입니다. 욕심이 게으른 것보다 빠른 이유는 온라인과 Friedl의 많은 책에서 설명하지만 여기에 OT가 있습니다. 어쩌면 다른 질문 (어떤?)이 실제 중복되지 않았습니까?
Abel

2
@Abel-계속 말하지만, 계속 듣지 않습니다. 나는 Friedl에 의해이 진술을 엄수한다 a functionality that won't be used in the majority of cases. 실제로 그것은 정규식 토지에서 가장 많이 찾는 기능입니다. 게으른 / 욕심? 내 의견과 어떤 관련이 있습니까? 가변 양의 캡처 버퍼를 가질 수 있습니다. 전체 문자열을 단일 일치로 스윕 할 수 있습니다. 경우 .*?(dog)발견은 첫 번째 dog다음 (?:.*?(dog))+발견 할 것이다 모든 dog 번의 매치에서 전체 문자열. 성능 향상이 눈에.니다.

20

그룹은 정규 표현식에서 그룹과 관련이있는 것입니다.

"(a[zx](b?))"

Applied to "axb" returns an array of 3 groups:

group 0: axb, the entire match.
group 1: axb, the first group matched.
group 2: b, the second group matched.

이것들은 단지 '포획 된'그룹이라는 것을 제외하고. 캡처하지 않은 그룹 ( '(? :'구문 사용)은 여기에 표시되지 않습니다.

"(a[zx](?:b?))"

Applied to "axb" returns an array of 2 groups:

group 0: axb, the entire match.
group 1: axb, the first group matched.

캡처는 또한 '캡처 된 그룹'과 관련된 것입니다. 그러나 그룹에 수량자를 여러 번 적용하면 마지막 일치 만 그룹의 일치로 유지됩니다. 캡처 배열은 이러한 모든 일치 항목을 저장합니다.

"(a[zx]\s+)+"

Applied to "ax az ax" returns an array of 2 captures of the second group.

group 1, capture 0 "ax "
group 1, capture 1 "az "

마지막 질문에 관해서는-Captures는 그들이 속한 그룹이 주문한 캡처의 배열이라고 생각했습니다. 오히려 이것은 그룹 [0] .Captures의 별명 일뿐입니다. 무용지물 ..


명확한 설명 (y)
Ghasan

19

간단한 예 (그림)로 설명 할 수 있습니다.

3:10pm정규식과 일치 하고 ((\d)+):((\d)+)(am|pm)Mono 대화식 사용 csharp:

csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)").
      > Groups.Cast<Group>().
      > Zip(Enumerable.Range(0, int.MaxValue), (g, n) => "[" + n + "] " + g);
{ "[0] 3:10pm", "[1] 3", "[2] 3", "[3] 10", "[4] 0", "[5] pm" }

그래서 1은 어디에 있습니까? 여기에 이미지 설명을 입력하십시오

네 번째 그룹에는 일치하는 여러 자릿수가 있으므로 그룹을 참조하는 경우 ( ToString()즉 , 암시 적 으로) 마지막 일치 만 "얻습니다" . 중간 일치 항목을 공개하려면 Captures해당 그룹 의 속성을 더 깊이 살펴보고 참조해야합니다 .

csharp> Regex.Match("3:10pm", @"((\d)+):((\d)+)(am|pm)").
      > Groups.Cast<Group>().
      > Skip(4).First().Captures.Cast<Capture>().
      > Zip(Enumerable.Range(0, int.MaxValue), (c, n) => "["+n+"] " + c);
{ "[0] 1", "[1] 0" }

여기에 이미지 설명을 입력하십시오

이 기사의 의례 .


3
좋은 기사. 그림은 천 단어의 가치가 있습니다.
AlexWei

당신은 스타입니다.
mikemay

14

MSDN 설명서에서 :

Captures 속성의 실제 유틸리티는 한정 그룹이 캡처 그룹에 적용되어 그룹이 단일 정규식으로 여러 하위 문자열을 캡처 할 때 발생합니다. 이 경우 Group 객체에는 마지막으로 캡처 된 하위 문자열에 대한 정보가 포함되고 Captures 속성에는 그룹이 캡처 한 모든 하위 문자열에 대한 정보가 포함됩니다. 다음 예에서는 정규식 \ b (\ w + \ s *) +입니다. 마침표로 끝나는 전체 문장과 일치합니다. 그룹 (\ w + \ s *) +은 모음의 개별 단어를 캡처합니다. Group 컬렉션에는 마지막으로 캡처 된 하위 문자열에 대한 정보 만 포함되므로 "문장"문장의 마지막 단어를 캡처합니다. 그러나 그룹에서 캡처 한 각 단어는 Captures 속성이 반환 한 컬렉션에서 사용할 수 있습니다.


4

다음과 같은 텍스트 입력 dogcatcatcat과 같은 패턴 이 있다고 상상해보십시오.dog(cat(catcat))

이 경우 그룹이 3 개이며 첫 번째 그룹 ( 주요 그룹 )이 일치합니다.

일치 == dogcatcatcat및 Group0 ==dogcatcatcat

Group1 == catcatcat

Group2 == catcat

무슨 일이야?

Regex클래스를 사용하여 C # (. NET)으로 작성된 작은 예제를 살펴 보겠습니다 .

int matchIndex = 0;
int groupIndex = 0;
int captureIndex = 0;

foreach (Match match in Regex.Matches(
        "dogcatabcdefghidogcatkjlmnopqr", // input
        @"(dog(cat(...)(...)(...)))") // pattern
)
{
    Console.Out.WriteLine($"match{matchIndex++} = {match}");

    foreach (Group @group in match.Groups)
    {
        Console.Out.WriteLine($"\tgroup{groupIndex++} = {@group}");

        foreach (Capture capture in @group.Captures)
        {
            Console.Out.WriteLine($"\t\tcapture{captureIndex++} = {capture}");
        }

        captureIndex = 0;
    }

    groupIndex = 0;
    Console.Out.WriteLine();
        }

출력 :

match0 = dogcatabcdefghi
    group0 = dogcatabcdefghi
        capture0 = dogcatabcdefghi
    group1 = dogcatabcdefghi
        capture0 = dogcatabcdefghi
    group2 = catabcdefghi
        capture0 = catabcdefghi
    group3 = abc
        capture0 = abc
    group4 = def
        capture0 = def
    group5 = ghi
        capture0 = ghi

match1 = dogcatkjlmnopqr
    group0 = dogcatkjlmnopqr
        capture0 = dogcatkjlmnopqr
    group1 = dogcatkjlmnopqr
        capture0 = dogcatkjlmnopqr
    group2 = catkjlmnopqr
        capture0 = catkjlmnopqr
    group3 = kjl
        capture0 = kjl
    group4 = mno
        capture0 = mno
    group5 = pqr
        capture0 = pqr

첫 번째 일치 항목 ( match0) 만 분석하겠습니다 .

당신이 볼 수 있듯이이 세 가지 있습니다 작은 그룹은 : group3, group4group5

    group3 = kjl
        capture0 = kjl
    group4 = mno
        capture0 = mno
    group5 = pqr
        capture0 = pqr

이러한 그룹 (3-5)은 기본 패턴 의 ' 서브 패턴 ' (...)(...)(...)으로 인해 생성되었습니다. (dog(cat(...)(...)(...)))

의 값은 group3캡처 ( capture0) 에 해당합니다 . ( group4및 의 경우 와 같이 group5). 같은 그룹 반복이 없기 때문 (...){3}입니다.


자, 그룹 반복 이있는 또 다른 예를 생각해 봅시다 .

우리가에서 (코드 위의 그림에 대한) 일치하는 정규 표현식 패턴을 수정하는 경우 (dog(cat(...)(...)(...)))(dog(cat(...){3})), 다음이 있음을 알 수 있습니다 그룹 반복 : (...){3}.

이제 출력 이 변경되었습니다.

match0 = dogcatabcdefghi
    group0 = dogcatabcdefghi
        capture0 = dogcatabcdefghi
    group1 = dogcatabcdefghi
        capture0 = dogcatabcdefghi
    group2 = catabcdefghi
        capture0 = catabcdefghi
    group3 = ghi
        capture0 = abc
        capture1 = def
        capture2 = ghi

match1 = dogcatkjlmnopqr
    group0 = dogcatkjlmnopqr
        capture0 = dogcatkjlmnopqr
    group1 = dogcatkjlmnopqr
        capture0 = dogcatkjlmnopqr
    group2 = catkjlmnopqr
        capture0 = catkjlmnopqr
    group3 = pqr
        capture0 = kjl
        capture1 = mno
        capture2 = pqr

다시, 첫 번째 일치 ( match0) 만 분석해 봅시다 .

이보다 더없는 작은 그룹 group4group5때문에 (...){3} 반복 ( {N} 항에있어서, N> = 2 )가 있었던 하나 개의 단일 그룹으로 병합 group3.

이 경우 group3값은 해당 값에 해당합니다 capture2( 즉, 마지막 캡처 ).

당신은 모든 3 내부 캡처를 필요 따라서 경우에 ( capture0, capture1, capture2) 당신은 그룹의 순환해야 Captures모음입니다.

결론 : 패턴 그룹을 디자인하는 방식에주의하십시오. 당신은 행동이 같은 그룹의 사양의 원인을 선불로 생각해야한다 (...)(...), (...){2}또는 (.{3}){2}


바라건대 Captures , GroupsMatches 의 차이점에 대한 정보를 얻을 수 있기를 바랍니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.