Câu hỏi Nhóm không bắt giữ là gì? (? :) làm gì?


Làm sao ?: được sử dụng và những gì nó tốt cho?


1327
2017-08-18 13:17


gốc


Câu hỏi này đã được thêm vào Câu hỏi thường gặp về biểu hiện cụm từ tràn ngăn xếp, trong "Nhóm". - aliteralmind


Các câu trả lời:


Hãy để tôi cố gắng giải thích điều này với một ví dụ.

Xem xét văn bản sau:

https://stackoverflow.com/
https://stackoverflow.com/questions/tagged/regex

Bây giờ, nếu tôi áp dụng regex dưới đây ...

(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

... Tôi sẽ nhận được kết quả như sau:

Match "https://stackoverflow.com/"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "http"
     Group 2: "stackoverflow.com"
     Group 3: "/questions/tagged/regex"

Nhưng tôi không quan tâm đến giao thức - tôi chỉ muốn máy chủ và đường dẫn của URL. Vì vậy, tôi thay đổi regex để bao gồm nhóm không chụp (?:).

(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?

Bây giờ, kết quả của tôi trông như thế này:

Match "https://stackoverflow.com/"
     Group 1: "stackoverflow.com"
     Group 2: "/"

Match "https://stackoverflow.com/questions/tagged/regex"
     Group 1: "stackoverflow.com"
     Group 2: "/questions/tagged/regex"

Xem? Nhóm đầu tiên chưa được chụp. Trình phân tích cú pháp sử dụng nó để khớp với văn bản, nhưng bỏ qua nó sau này, trong kết quả cuối cùng.


CHỈNH SỬA:

Theo yêu cầu, hãy để tôi cố gắng giải thích các nhóm.

Vâng, các nhóm phục vụ nhiều mục đích. Chúng có thể giúp bạn trích xuất thông tin chính xác từ một kết hợp lớn hơn (cũng có thể được đặt tên), chúng cho phép bạn sắp xếp lại một nhóm phù hợp trước đó và có thể được sử dụng để thay thế. Hãy thử một số ví dụ, phải không?

Ok, hãy tưởng tượng bạn có một số loại XML hoặc HTML (lưu ý rằng regex có thể không phải là công cụ tốt nhất cho công việc, nhưng nó là tốt đẹp như là một ví dụ). Bạn muốn phân tích các thẻ, vì vậy bạn có thể làm một cái gì đó như thế này (tôi đã thêm không gian để làm cho nó dễ hiểu hơn):

   \<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
   \<(.+?)\> [^<]*? \</\1\>

Regex đầu tiên có một nhóm được đặt tên (TAG), trong khi nhóm thứ hai sử dụng một nhóm chung. Cả hai regex đều làm điều tương tự: chúng sử dụng giá trị từ nhóm đầu tiên (tên của thẻ) để khớp với thẻ đóng. Sự khác biệt là người đầu tiên sử dụng tên để khớp với giá trị và giá trị thứ hai sử dụng chỉ mục nhóm (bắt đầu từ 1).

Hãy thử một số thay thế ngay bây giờ. Xem xét văn bản sau:

Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.

Bây giờ, hãy sử dụng regex ngu ngốc này trên nó:

\b(\S)(\S)(\S)(\S*)\b

Regex này phù hợp với các từ có ít nhất 3 ký tự và sử dụng các nhóm để phân tách ba chữ cái đầu tiên. Kết quả là:

Match "Lorem"
     Group 1: "L"
     Group 2: "o"
     Group 3: "r"
     Group 4: "em"
Match "ipsum"
     Group 1: "i"
     Group 2: "p"
     Group 3: "s"
     Group 4: "um"
...

Match "consectetuer"
     Group 1: "c"
     Group 2: "o"
     Group 3: "n"
     Group 4: "sectetuer"
...

Vì vậy, nếu chúng ta áp dụng chuỗi thay thế ...

$1_$3$2_$4

... trên đó, chúng tôi đang cố gắng sử dụng nhóm đầu tiên, thêm dấu gạch dưới, sử dụng nhóm thứ ba, sau đó là nhóm thứ hai, thêm một dấu gạch dưới khác, và sau đó là nhóm thứ tư. Chuỗi kết quả sẽ giống như chuỗi bên dưới.

L_ro_em i_sp_um d_lo_or s_ti_ a_em_t c_no_sectetuer f_ue_giat f_ma_es m_la_esuada p_er_tium e_eg_stas.

Bạn cũng có thể sử dụng các nhóm được đặt tên để thay thế, bằng cách sử dụng ${name}.

Để chơi xung quanh với regexes, tôi khuyên bạn nên http://regex101.com/, cung cấp một lượng chi tiết tốt về cách thức hoạt động của regex; nó cũng cung cấp một vài động cơ regex để bạn lựa chọn.


1839
2017-08-18 15:39



@ajsie: Các nhóm truyền thống (bắt giữ) hữu ích nhất nếu bạn đang thực hiện thao tác thay thế trên kết quả. Dưới đây là một ví dụ mà tôi lấy tên và họ được phân cách bằng dấu phẩy và sau đó đảo ngược thứ tự của chúng (nhờ các nhóm được đặt tên) ... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30 - Steve Wortham
Không, nó không giống nhau. - Ricardo Nolde
Cũng có thể chỉ ra rằng các nhóm không bắt giữ là hữu ích duy nhất khi sử dụng regex làm dấu tách phân tách: "Alice và Bob" -split "\ s + (?: và | hoặc) \ s +" - Yevgeniy
Sẽ rất thú vị khi có sự khác biệt giữa các nhóm không bắt giữ (? :), và các xác nhận lookahead và lookbehind (? =,?!) Được giải thích. Tôi bắt đầu học về các biểu thức chính quy, nhưng từ những gì tôi hiểu, các nhóm không bắt được sử dụng để đối sánh và "trả về" những gì chúng khớp, nhưng "giá trị trả về" không được "lưu trữ" để tham khảo lại. Mặt khác, các xác nhận lookahead và lookbehind không chỉ là "được lưu trữ", chúng cũng không phải là một phần của một trận đấu, chúng chỉ khẳng định rằng một cái gì đó sẽ khớp, nhưng giá trị "match" của chúng bị bỏ qua, nếu tôi không nhầm. (Tôi có đúng không?) - Christian
[] là một bộ; [123] khớp với bất kỳ char nào trong tập hợp một lần; [^ 123] khớp với bất cứ thứ gì KHÔNG nằm trong tập hợp một lần; [^ / \ r \ n] + khớp với một hoặc nhiều ký tự khác với /, \ r, \ n. - Ricardo Nolde


Bạn có thể sử dụng các nhóm chụp để sắp xếp và phân tích cú pháp một biểu thức. Một nhóm không bắt giữ có lợi ích đầu tiên, nhưng không có chi phí thứ hai. Bạn vẫn có thể nói một nhóm không chụp là tùy chọn, ví dụ.

Giả sử bạn muốn khớp với văn bản số, nhưng một số số có thể được viết dưới dạng 1, 2, 3, 4, ... Nếu bạn muốn nắm bắt phần số, nhưng không phải hậu tố (tùy chọn), bạn có thể sử dụng nhóm không chụp .

([0-9]+)(?:st|nd|rd|th)?

Điều đó sẽ khớp với các số ở dạng 1, 2, 3 ... hoặc ở dạng 1, 2, 3, ... nhưng nó sẽ chỉ nắm bắt phần số.


135
2017-08-18 13:24





?: được sử dụng khi bạn muốn nhóm một biểu thức, nhưng bạn không muốn lưu nó như là một phần phù hợp / đã bắt của chuỗi.

Một ví dụ sẽ là một cái gì đó để phù hợp với một địa chỉ IP:

/(?:\d{1,3}\.){3}\d{1,3}/

Lưu ý rằng tôi không quan tâm đến việc lưu 3 octet đầu tiên, nhưng (?:...) nhóm cho phép tôi rút ngắn regex mà không phải gánh chịu chi phí thu giữ và lưu trữ một kết quả phù hợp.


87
2017-08-18 13:22





Nó làm cho nhóm không chụp, có nghĩa là chuỗi con phù hợp với nhóm đó sẽ không được đưa vào danh sách các ảnh chụp. Một ví dụ trong ruby ​​để minh họa sự khác biệt:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]

27
2017-08-18 13:23





ĐỘNG CƠ HISTORICAL: Sự tồn tại của các nhóm không bắt giữ có thể được giải thích bằng cách sử dụng dấu ngoặc đơn. Xem xét các biểu thức (a | b) c và a | bc, do ưu tiên nối trên |, các biểu thức này đại diện cho hai ngôn ngữ khác nhau ({ac, bc} và {a, bc} tương ứng). Tuy nhiên, dấu ngoặc đơn cũng được sử dụng như một nhóm phù hợp (như được giải thích bởi các câu trả lời khác ...).

Khi bạn muốn có dấu ngoặc đơn nhưng không nắm bắt được biểu thức con, bạn sử dụng các NHÓM KHÔNG NĂNG LỰC. Trong ví dụ, (?: A | b) c


14
2018-02-04 08:07



Tôi đã tự hỏi tại sao. Khi tôi nghĩ "lý do" là rất quan trọng để ghi nhớ thông tin này. - J.M.I. MADISON


Các nhóm chụp bạn có thể sử dụng sau này trong regex để khớp HOẶC LÀ bạn có thể sử dụng chúng trong phần thay thế của regex. Tạo một không bắt nhóm chỉ đơn giản là loại bỏ nhóm đó khỏi bị sử dụng vì một trong những lý do này.

Các nhóm không chụp là tuyệt vời nếu bạn đang cố chụp nhiều thứ khác nhau và có một số nhóm bạn không muốn chụp.

Thats khá nhiều lý do họ tồn tại. Trong khi bạn đang tìm hiểu về các nhóm, hãy tìm hiểu về Nhóm nguyên tử, họ làm rất nhiều! Ngoài ra còn có các nhóm nhìn chung nhưng chúng phức tạp hơn một chút và không được sử dụng quá nhiều.

Ví dụ về việc sử dụng sau này trong regex (backreference):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>  [Tìm một thẻ xml (không hỗ trợ ns)]

([A-Z][A-Z0-9]*) là nhóm chụp (trong trường hợp này, đó là tên thẻ)

Sau đó trong regex là \1 có nghĩa là nó sẽ chỉ khớp với cùng một văn bản trong nhóm đầu tiên ( ([A-Z][A-Z0-9]*) nhóm) (trong trường hợp này nó phù hợp với thẻ kết thúc).


12
2017-08-18 13:22



bạn có thể đưa ra một ví dụ đơn giản về cách nó sẽ được sử dụng sau này để khớp với OR không? - never_had_a_name
tôi có nghĩa là bạn có thể sử dụng để phù hợp với sau này hoặc bạn có thể sử dụng nó để thay thế. Hoặc trong câu đó chỉ cho bạn thấy có hai cách sử dụng cho nhóm chụp - Bob Fincheimer


Hãy để tôi thử điều này với một ví dụ: -

Mã Regex: - (?:animal)(?:=)(\w+)(,)\1\2

Chuỗi tìm kiếm :-

Dòng 1 - động vật = mèo, chó, mèo, hổ, chó

Dòng 2 - động vật = mèo, mèo, chó, chó, hổ

Dòng 3 - động vật = chó, chó, mèo, mèo, hổ

(?:animal) -> Nhóm chưa chụp 1

(?:=)-> Nhóm không chụp 2

(\w+)-> Nhóm chụp 1

(,)-> Nhóm đã chụp 2

\1 -> Kết quả của nhóm bị bắt 1 trong Dòng 1 là mèo, Trong Dòng 2 là mèo, Trong Dòng 3 là chó.

\2 -> kết quả của nhóm bị bắt 2 tức là dấu phẩy (,)

Vì vậy, trong đoạn mã này bằng cách cho \ 1 và \ 2 chúng ta nhớ lại hoặc lặp lại kết quả của nhóm được chụp 1 và 2 tương ứng sau trong mã.

Theo thứ tự mã (?: Động vật) nên là nhóm 1 và (?: =) Phải là nhóm 2 và tiếp tục ..

nhưng bằng cách đưa ra?: chúng tôi làm cho nhóm đối sánh không bị bắt (không được tính trong nhóm được so khớp, vì vậy số nhóm bắt đầu từ nhóm được chụp đầu tiên chứ không phải nhóm không bị bắt) để lặp lại kết quả của trận đấu -nhóm (?: động vật) không thể được gọi sau này trong mã.

Hy vọng điều này giải thích việc sử dụng nhóm không chụp.

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


8
2018-01-19 11:36





Tôi cũng là một nhà phát triển JavaScript và sẽ cố gắng giải thích ý nghĩa của nó liên quan đến JavaScript.

Xem xét một kịch bản mà bạn muốn khớp cat is animal khi bạn muốn phù hợp với mèo và động vật và cả hai nên có một is ở giữa chúng.

 // this will ignore "is" as that's is what we want
"cat is animal".match(/(cat)(?: is )(animal)/) ;
result ["cat is animal", "cat", "animal"]

 // using lookahead pattern it will match only "cat" we can
 // use lookahead but the problem is we can not give anything
 // at the back of lookahead pattern
"cat is animal".match(/cat(?= is animal)/) ;
result ["cat"]

 //so I gave another grouping parenthesis for animal
 // in lookahead pattern to match animal as well
"cat is animal".match(/(cat)(?= is (animal))/) ;
result ["cat", "cat", "animal"]

 // we got extra cat in above example so removing another grouping
"cat is animal".match(/cat(?= is (animal))/) ;
result ["cat", "animal"]

6
2018-03-01 09:43





Trong các biểu thức chính quy phức tạp, bạn có thể có tình huống phát sinh ở nơi bạn muốn sử dụng một số lượng lớn các nhóm trong đó có một số nhóm để kết hợp lặp lại và một số trong đó có để cung cấp các tham chiếu ngược. Theo mặc định, văn bản phù hợp với mỗi nhóm được nạp vào mảng backreference. Trong trường hợp chúng ta có nhiều nhóm và chỉ cần tham khảo một số trong số chúng từ mảng backreference, chúng ta có thể ghi đè hành vi mặc định này để cho biểu thức chính quy biết rằng các nhóm nhất định chỉ có để xử lý lặp lại và không cần phải được capture và lưu trữ trong mảng backreference.


5
2018-03-08 17:33





Một điều thú vị mà tôi gặp phải là bạn có thể có một nhóm chụp bên trong một nhóm không bắt giữ. Hãy xem regex bên dưới để tìm các url web phù hợp:

var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Chuỗi url nhập:

var url = "http://www.ora.com:80/goodparts?q#fragment";

Nhóm đầu tiên trong regex của tôi (?:([A-Za-z]+):) là nhóm không chụp phù hợp với lược đồ giao thức và dấu hai chấm : nhân vật tức là http: nhưng khi tôi đang chạy dưới mã, tôi đã nhìn thấy chỉ số 1 của mảng được trả về có chứa chuỗi http khi tôi đang nghĩ rằng http và đại tràng : cả hai sẽ không được báo cáo vì chúng nằm trong một nhóm không bắt giữ.

console.debug(parse_url_regex.exec(url));

enter image description here

Tôi nghĩ nếu nhóm đầu tiên (?:([A-Za-z]+):) là một nhóm không bắt được thì tại sao nó quay trở lại http chuỗi trong mảng đầu ra.

Vì vậy, nếu bạn nhận thấy rằng có một nhóm lồng nhau ([A-Za-z]+) bên trong nhóm không chụp. Nhóm lồng nhau ([A-Za-z]+) là nhóm chụp (không có ?: ngay từ đầu) bên trong một nhóm không bắt (?:([A-Za-z]+):). Đó là lý do tại sao văn bản http vẫn bị bắt nhưng đại tràng : ký tự nằm bên trong nhóm không chụp nhưng bên ngoài nhóm chụp không được báo cáo trong mảng đầu ra.


4
2017-07-15 03:13





tl; dr các nhóm không bắt giữ, như tên cho thấy là các phần của regex mà bạn không muốn được bao gồm trong trận đấu và ?: là một cách để xác định một nhóm là không bắt.

Giả sử bạn có địa chỉ email example@example.com. Regex sau sẽ tạo hai các nhóm, phần id và phần @ example.com. (\p{Alpha}*[a-z])(@example.com). Vì mục đích đơn giản, chúng tôi đang trích xuất toàn bộ tên miền bao gồm cả @ tính cách.

Bây giờ, giả sử bạn chỉ cần phần id của địa chỉ. Những gì bạn muốn làm là lấy nhóm đầu tiên của kết quả trận đấu, được bao quanh bởi () trong regex và cách để làm điều này là sử dụng cú pháp nhóm không bắt, tức là ?:. Vì vậy, các regex (\p{Alpha}*[a-z])(?:@example.com) sẽ chỉ trả về phần id của email.


3
2018-05-11 05:27