Có gì khác biệt giữa các nhóm của nhóm Cameron và nhóm bắt giữ trong các biểu thức chính quy của .NET?


161

Tôi hơi mơ hồ về sự khác biệt giữa "nhóm" và "chụp" là gì khi nói đến ngôn ngữ biểu thức chính quy của .NET. Hãy xem xét mã C # sau:

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

Tôi hy vọng điều này sẽ dẫn đến một lần chụp cho chữ 'Q', nhưng nếu tôi in các thuộc tính của trả lại MatchCollection, tôi thấy:

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

Chính xác thì chuyện gì đang xảy ra ở đây vậy? Tôi hiểu rằng cũng có một bản chụp cho toàn bộ trận đấu, nhưng làm thế nào để các nhóm tham gia? Và tại sao không matches[0].Capturesbao gồm chụp cho chữ 'Q'?

Câu trả lời:


126

Bạn sẽ không phải là người đầu tiên mờ nhạt về nó. Đây là những gì Jeffrey Friedl nổi tiếng nói về nó (trang 437+):

Tùy thuộc vào quan điểm của bạn, nó sẽ thêm một chiều mới thú vị cho kết quả trận đấu hoặc thêm sự nhầm lẫn và phình to.

Và hơn thế nữa:

Sự khác biệt chính giữa một đối tượng Nhóm và đối tượng Capture là mỗi đối tượng Nhóm chứa một tập hợp các Biểu tượng đại diện cho tất cả các kết quả trung gian của nhóm trong trận đấu, cũng như văn bản cuối cùng được nhóm khớp.

Và một vài trang sau, đây là kết luận của anh ấy:

Sau khi vượt qua tài liệu .NET và thực sự hiểu những gì các đối tượng này thêm vào, tôi đã có những cảm xúc lẫn lộn về chúng. Một mặt, đó là một sự đổi mới thú vị [..] mặt khác, nó dường như thêm gánh nặng hiệu quả [..] của một chức năng sẽ không được sử dụng trong phần lớn các trường hợp

Nói cách khác: chúng rất giống nhau, nhưng đôi khi và khi nó xảy ra, bạn sẽ tìm thấy cách sử dụng chúng. Trước khi bạn mọc một bộ râu xám khác, bạn thậm chí có thể thích các Chụp ...


Vì không phải ở trên, cũng không phải những gì được nói trong bài đăng khác thực sự có vẻ như trả lời câu hỏi của bạn, hãy xem xét những điều sau đây. Hãy nghĩ về Chụp như một loại theo dõi lịch sử. Khi regex thực hiện khớp, nó sẽ đi qua chuỗi từ trái sang phải (bỏ qua việc quay lại trong giây lát) và khi nó gặp một dấu ngoặc đơn bắt giữ khớp, nó sẽ lưu trữ trong $x(x là bất kỳ chữ số nào), giả sử $1.

Các công cụ regex thông thường, khi lặp lại các dấu ngoặc đơn, sẽ loại bỏ dòng điện $1và sẽ thay thế nó bằng giá trị mới. Không phải .NET, sẽ giữ lịch sử này và đặt nó vào Captures[0].

Nếu chúng tôi thay đổi regex của bạn để trông như sau:

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

bạn sẽ nhận thấy rằng nhóm đầu tiên Groupsẽ có một Captures(nhóm đầu tiên luôn luôn là toàn bộ trận đấu, tức là bằng $0) và nhóm thứ hai sẽ giữ {S}, tức là chỉ nhóm phù hợp cuối cùng. Tuy nhiên, và đây là sản phẩm khai thác, nếu bạn muốn tìm hai sản phẩm khai thác khác, chúng sẽ Captureschứa tất cả các sản phẩm khai thác trung gian cho {Q} {R}{S}.

Nếu bạn từng tự hỏi làm thế nào bạn có thể nhận được từ nhiều lần chụp, chỉ hiển thị kết quả khớp cuối cùng với các ảnh chụp riêng lẻ có trong chuỗi, bạn phải sử dụng Captures.

Một từ cuối cùng cho câu hỏi cuối cùng của bạn: tổng số trận đấu luôn có một Tổng số Bắt giữ, không trộn lẫn với các Nhóm riêng lẻ. Chụp chỉ thú vị trong nhóm .


1
a functionality that won't be used in the majority of casesTôi nghĩ rằng anh ấy đã bỏ lỡ chiếc thuyền. Trong ngắn hạn, (?:.*?(collection info)){4,20}tăng hiệu quả lên vài trăm phần trăm.

1
@sln, không chắc bạn đang đề cập đến cái gì và 'anh ấy' là ai (Friedl?). Ví dụ bạn đưa ra dường như không liên quan đến cuộc thảo luận này hoặc với các biểu thức được sử dụng. Bên cạnh đó, các bộ lượng hóa không tham lam chỉ rất hiếm khi hiệu quả hơn các bộ lượng hóa tham lam, và đòi hỏi kiến ​​thức về bộ đầu vào và kiểm tra sự hoàn hảo cẩn thận.
Abel

@Abel - Tôi đã đến đây từ một câu hỏi được đánh dấu trùng lặp này. Tôi thấy Friedl trích dẫn. Bài đăng này đã cũ và cần được làm mới để giữ cho nó hiện đại. Chỉ với Dot Net, điều này mới có thể được thực hiện, đó là những gì tách biệt với hầu hết những người khác. Breakdown: Một ví dụ nhóm tổng thể không nắm bắt được định lượng (?:..)+. Lazily phù hợp với bất cứ điều gì .*?lên đến một biểu thức phụ chụp (nhóm). Tiếp tục. Trong một trận đấu duy nhất, một bộ sưu tập nhóm kết tủa một loạt những thứ cần thiết. Không có nhu cầu tìm kiếm tiếp theo, không có lối vào lại làm cho nó nhanh hơn 10 đến 20 lần.

1
@sln, câu hỏi này là về một cái gì đó khác và nó đặc biệt về một tính năng .net không được tìm thấy trong các công cụ regex khác (nhóm so với chụp, xem tiêu đề). Tôi không thấy bất cứ điều gì lỗi thời ở đây, .net vẫn hoạt động như cũ, trên thực tế phần này đã không thay đổi trong một thời gian dài trong .net. Hiệu suất không phải là một phần của câu hỏi. Vâng, không bắt nhóm là nhanh hơn, nhưng một lần nữa, chủ đề ở đây là ngược lại. Tại sao tham lam nhanh hơn lười biếng được giải thích trong nhiều văn bản trực tuyến và bởi cuốn sách của Friedl, nhưng OT ở đây. Có lẽ câu hỏi khác (mà?) Không phải là một bản sao thực sự?
Abel

2
@ Tin - Tôi biết tôi tiếp tục nói, nhưng bạn không nghe thấy. Tôi rất tin vào tuyên bố này của Friedl a functionality that won't be used in the majority of cases. Trong thực tế, nó là chức năng được tìm kiếm nhiều nhất trong vùng đất regex. Lười biếng / tham lam? Điều đó có liên quan gì đến ý kiến ​​của tôi? Nó cho phép có một số lượng bộ đệm chụp khác nhau. Nó có thể quét toàn bộ chuỗi trong một trận đấu. Nếu .*?(dog)tìm thấy đầu tiên dogthì (?:.*?(dog))+sẽ tìm thấy tất cả dog trong toàn bộ chuỗi trong một trận đấu. Sự gia tăng hiệu suất là đáng chú ý.

20

Nhóm là những gì chúng tôi đã liên kết với các nhóm trong biểu thức chính quy

"(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.

ngoại trừ việc đây chỉ là những nhóm 'bị bắt'. Các nhóm không chụp (sử dụng cú pháp '(?:' Không được trình bày ở đây.

"(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.

Chụp cũng là những gì chúng tôi đã liên kết với 'các nhóm bị bắt'. Nhưng khi nhóm được áp dụng với bộ định lượng nhiều lần, chỉ có trận đấu cuối cùng được giữ làm trận đấu của nhóm. Các mảng chụp lưu trữ tất cả các trận đấu.

"(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 "

Đối với câu hỏi cuối cùng của bạn - tôi đã có thể nghĩ trước khi xem xét điều này rằng Chụp sẽ là một loạt các hình ảnh chụp theo yêu cầu của nhóm mà họ thuộc về. Thay vào đó, nó chỉ là một bí danh cho các nhóm [0] .Captures. Khá vô dụng ..


Giải thích rõ ràng (y)
Ghasan

19

Điều này có thể được giải thích với một ví dụ đơn giản (và hình ảnh).

Phù hợp 3:10pmvới biểu thức chính quy ((\d)+):((\d)+)(am|pm)và sử dụng tương tác 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" }

Vậy đâu là số 1? nhập mô tả hình ảnh ở đây

Vì có nhiều chữ số khớp với nhóm thứ tư, chúng tôi chỉ "nhận được" trận đấu cuối cùng nếu chúng tôi tham chiếu nhóm (với một ẩn ToString(), đó là). Để hiển thị các trận đấu trung gian, chúng tôi cần đi sâu hơn và tham chiếu Capturestài sản trên nhóm được đề cập:

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" }

nhập mô tả hình ảnh ở đây

Lịch sự của bài viết này .


3
Điều tốt đẹp. Một bưc tranh đang gia ngan lơi noi.
AlexWei

Bạn là một ngôi sao.
mikemay

14

Từ tài liệu MSDN :

Tiện ích thực sự của thuộc tính Chụp xảy ra khi một bộ định lượng được áp dụng cho một nhóm bắt để nhóm bắt được nhiều chuỗi con trong một biểu thức chính quy duy nhất. Trong trường hợp này, đối tượng Nhóm chứa thông tin về chuỗi con bị bắt cuối cùng, trong khi thuộc tính Chụp có chứa thông tin về tất cả các chuỗi con được nhóm nắm bắt. Trong ví dụ sau, biểu thức chính quy \ b (\ w + \ s *) +. phù hợp với toàn bộ câu kết thúc trong một khoảng thời gian. Nhóm (\ w + \ s *) + nắm bắt các từ riêng lẻ trong bộ sưu tập. Bởi vì bộ sưu tập Nhóm chỉ chứa thông tin về chuỗi con được bắt cuối cùng, nên nó thu được từ cuối cùng trong câu, "câu". Tuy nhiên, mỗi từ được nhóm thu thập có sẵn từ bộ sưu tập được trả về bởi thuộc tính Chụp.


4

Hãy tưởng tượng bạn có kiểu nhập văn bản sau dogcatcatcatvà một mẫu nhưdog(cat(catcat))

Trong trường hợp này, bạn có 3 nhóm, nhóm đầu tiên ( nhóm chính ) tương ứng với trận đấu.

Khớp == dogcatcatcatvà Group0 ==dogcatcatcat

Nhóm1 == catcatcat

Nhóm2 == catcat

Vì vậy, tất cả những gì về?

Chúng ta hãy xem xét một ví dụ nhỏ được viết bằng C # (.NET) bằng cách sử dụng Regexlớp.

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();
        }

Đầu ra :

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

Hãy phân tích trận đấu đầu tiên ( match0).

Như bạn có thể thấy có ba nhóm nhỏ : group3, group4group5

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

Những nhóm đó (3-5) đã được tạo vì ' mẫu con ' (...)(...)(...)của mẫu chính (dog(cat(...)(...)(...)))

Giá trị group3tương ứng với nó chụp ( capture0). (Như trong trường hợp group4group5). Đó là bởi vì không có sự lặp lại nhóm như thế nào (...){3}.


Ok, hãy xem xét một ví dụ khác khi có sự lặp lại của nhóm .

Nếu chúng ta sửa đổi các mẫu biểu thức chính quy để được xuất hiện (đối với mã hiển thị ở trên) từ (dog(cat(...)(...)(...)))đến (dog(cat(...){3})), bạn sẽ nhận thấy rằng có những điều sau nhóm lặp lại : (...){3}.

Bây giờ Đầu ra đã thay đổi:

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

Một lần nữa, hãy phân tích trận đấu đầu tiên ( match0).

Không còn các nhóm nhỏ nữa group4group5(...){3} sự lặp lại ( {n} trong đó n> = 2 ) chúng đã được hợp nhất thành một nhóm duy nhất group3.

Trong trường hợp này, group3giá trị tương ứng với nó capture2( lần chụp cuối cùng , nói cách khác).

Vì vậy, nếu bạn cần tất cả 3 ảnh chụp bên trong ( capture0, capture1, capture2), bạn sẽ phải lướt qua của nhóm Capturesbộ sưu tập.

Loại trừ là: chú ý đến cách bạn thiết kế các nhóm mẫu của bạn. Bạn nên suy nghĩ trước những hành vi gây ra đặc điểm kỹ thuật của nhóm, như (...)(...), (...){2}hoặc (.{3}){2}vv


Hy vọng rằng nó cũng sẽ giúp làm sáng tỏ sự khác biệt giữa Chụp , NhómTrận đấu .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.