Apa yang dimaksud dengan grup tidak menangkap dalam ekspresi reguler?

Jawaban:

2329

Biarkan saya mencoba menjelaskan ini dengan sebuah contoh.

Pertimbangkan teks berikut:

http://stackoverflow.com/
/programming/tagged/regex

Sekarang, jika saya menerapkan regex di bawah ini ...

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

... Saya akan mendapatkan hasil berikut:

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

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

Tapi saya tidak peduli dengan protokol - saya hanya ingin host dan path dari URL. Jadi, saya mengubah regex untuk memasukkan grup yang tidak menangkap (?:).

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

Sekarang, hasil saya terlihat seperti ini:

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

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

Lihat? Grup pertama belum ditangkap. Parser menggunakannya untuk mencocokkan teks, tetapi mengabaikannya nanti, pada hasil akhir.


EDIT:

Seperti yang diminta, izinkan saya mencoba menjelaskan kelompok juga.

Ya, kelompok melayani banyak tujuan. Mereka dapat membantu Anda untuk mengekstrak informasi yang tepat dari kecocokan yang lebih besar (yang juga bisa disebut), mereka memungkinkan Anda membuat ulang grup yang cocok sebelumnya, dan dapat digunakan untuk pergantian pemain. Mari kita coba beberapa contoh, oke?

Bayangkan Anda memiliki semacam XML atau HTML (perlu diketahui bahwa regex mungkin bukan alat terbaik untuk pekerjaan itu , tetapi itu bagus sebagai contoh). Anda ingin mengurai tag, sehingga Anda dapat melakukan sesuatu seperti ini (saya telah menambahkan spasi untuk membuatnya lebih mudah dimengerti):

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

Regex pertama memiliki grup bernama (TAG), sedangkan regex kedua menggunakan grup umum. Kedua regex melakukan hal yang sama: mereka menggunakan nilai dari grup pertama (nama tag) untuk mencocokkan tag penutup. Perbedaannya adalah bahwa yang pertama menggunakan nama untuk mencocokkan nilai, dan yang kedua menggunakan indeks grup (yang dimulai pada 1).

Mari kita coba beberapa pergantian sekarang. Pertimbangkan teks berikut:

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

Sekarang, mari kita gunakan regex bodoh ini di atasnya:

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

Regex ini cocok dengan kata-kata dengan setidaknya 3 karakter, dan menggunakan grup untuk memisahkan tiga huruf pertama. Hasilnya adalah ini:

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

Jadi, jika kita menerapkan string substitusi:

$1_$3$2_$4

... di atasnya, kami mencoba menggunakan grup pertama, tambahkan garis bawah, gunakan grup ketiga, lalu grup kedua, tambahkan garis bawah lain, dan kemudian grup keempat. String yang dihasilkan akan seperti yang di bawah ini.

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.

Anda juga dapat menggunakan grup yang disebut sebagai pengganti, menggunakan ${name}.

Untuk bermain-main dengan regex , saya sarankan http://regex101.com/ , yang menawarkan sejumlah detail tentang cara kerja regex; itu juga menawarkan beberapa mesin regex untuk dipilih.

Ricardo Nolde
sumber
3
@ajsie: Grup tradisional (menangkap) paling berguna jika Anda melakukan operasi penggantian pada hasilnya. Berikut adalah contoh di mana saya mengambil nama belakang & depan yang dipisahkan koma dan kemudian membalikkan pesanan mereka (terima kasih kepada grup yang disebutkan) ... regexhero.net/tester/?id=16892996-64d4-4f10-860a-24f28dad7e30
Steve Wortham
2
Tidak, tidak sama.
Ricardo Nolde
4
Mungkin juga menunjukkan bahwa kelompok yang tidak menangkap secara unik berguna ketika menggunakan regex sebagai pembatas terpisah: "Alice and Bob" -split "\ s + (?: dan | atau) \ s +"
Yevgeniy
7
Akan menarik untuk memiliki perbedaan antara kelompok-kelompok yang tidak menangkap (? :), dan lookahead dan melihat di belakang pernyataan (? =,?!) Yang dijelaskan. Saya baru saja mulai belajar tentang ekspresi reguler, tetapi dari apa yang saya pahami, kelompok yang tidak menangkap digunakan untuk mencocokkan dan "mengembalikan" apa yang cocok, tetapi "nilai pengembalian" tidak "disimpan" untuk referensi-kembali. Lihat di muka dan lihat di belakang pernyataan di sisi lain tidak hanya tidak "disimpan", mereka juga bukan bagian dari pertandingan, mereka hanya menyatakan bahwa sesuatu akan cocok, tetapi nilai "pertandingan" mereka diabaikan, jika saya tidak salah .. (Apakah saya kira benar?)
Christian
5
[] adalah satu set; [123] cocok dengan arang apa pun di dalam set sekali; [^ 123] cocok dengan apa pun yang TIDAK di dalam himpunan satu kali; [^ / \ r \ n] + cocok dengan satu atau beberapa karakter yang berbeda dari /, \ r, \ n.
Ricardo Nolde
180

Anda dapat menggunakan menangkap grup untuk mengatur dan mengurai ekspresi. Grup yang tidak menangkap memiliki manfaat pertama, tetapi tidak memiliki overhead yang kedua. Anda masih dapat mengatakan bahwa grup yang tidak menangkap adalah opsional, misalnya.

Katakanlah Anda ingin mencocokkan teks numerik, tetapi beberapa angka dapat ditulis sebagai 1, 2, 3, 4, ... Jika Anda ingin mengambil bagian numerik, tetapi bukan sufiks (opsional), Anda dapat menggunakan grup yang tidak menangkap .

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

Itu akan cocok dengan angka dalam formulir 1, 2, 3 ... atau dalam bentuk 1, 2, 3, ... tetapi itu hanya akan menangkap bagian numerik.

Bill the Lizard
sumber
3
Ringkas dan mungkin penjelasan terbaik di sini.
NelsonGon
107

?: digunakan ketika Anda ingin mengelompokkan ekspresi, tetapi Anda tidak ingin menyimpannya sebagai bagian string yang cocok / ditangkap.

Contohnya adalah sesuatu yang cocok dengan alamat IP:

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

Perhatikan bahwa saya tidak peduli tentang menyimpan 3 oktet pertama, tetapi (?:...)pengelompokan memungkinkan saya untuk mempersingkat regex tanpa mengeluarkan biaya tambahan untuk menangkap dan menyimpan korek api.

RC.
sumber
38

Itu membuat grup tidak menangkap, yang berarti bahwa substring yang cocok dengan grup itu tidak akan dimasukkan dalam daftar tangkapan. Contoh dalam ruby ​​untuk menggambarkan perbedaan:

"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
sepp2k
sumber
Mengapa kita tidak bisa menggunakan "abc" .match (/.(.)./). Menangkap di sini?
PRASANNA SARAF
@PRASANNASARAF Anda tentu saja bisa. Maksud kode adalah untuk menunjukkan bahwa (?:)tidak menghasilkan tangkapan, bukan untuk menunjukkan contoh yang bermanfaat (?:). (?:)berguna ketika Anda ingin mengelompokkan sub-ekspresi (misalnya ketika Anda ingin menerapkan quantifiers ke sub-ekspresi non-atom atau jika Anda ingin membatasi ruang lingkup a |), tetapi Anda tidak ingin menangkap apa pun.
sepp2k
26

MOTIVASI SEJARAH:

Keberadaan kelompok yang tidak menangkap dapat dijelaskan dengan menggunakan tanda kurung.

Pertimbangkan ungkapan-ungkapan (a|b)cdan a|bc, karena prioritas rangkaian diakhiri |, ungkapan-ungkapan ini mewakili dua bahasa yang berbeda ( {ac, bc}dan {a, bc}masing - masing).

Namun, tanda kurung juga digunakan sebagai kelompok yang cocok (seperti yang dijelaskan oleh jawaban lain ...).

Ketika Anda ingin memiliki tanda kurung tetapi tidak menangkap sub-ekspresi Anda menggunakan KELOMPOK NON-CAPTURING. Dalam contoh,(?:a|b)c

pengguna2369060
sumber
6
Saya bertanya-tanya mengapa. Karena saya pikir "mengapa" sangat penting untuk menghafal informasi ini.
JMI MADISON
22

Biarkan saya coba ini dengan sebuah contoh:

Kode Regex: (?:animal)(?:=)(\w+)(,)\1\2

Cari String:

Baris 1 - animal=cat,dog,cat,tiger,dog

Baris 2 - animal=cat,cat,dog,dog,tiger

Baris 3 - animal=dog,dog,cat,cat,tiger

(?:animal) -> Grup Non-Tertangkap 1

(?:=)-> Grup Non-Tertangkap 2

(\w+)-> Grup Yang Ditangkap 1

(,)-> Grup Yang Ditangkap 2

\1 -> hasil tangkapan kelompok 1 yaitu Jalur 1 adalah kucing, Baris 2 adalah kucing, Baris 3 adalah anjing.

\2 -> hasil tangkapan kelompok 2 yaitu koma (,)

Jadi dalam kode ini dengan memberi \1dan \2kami mengingat atau mengulangi hasil dari kelompok yang ditangkap 1 dan 2 masing-masing kemudian dalam kode.

Sesuai urutan kode (?:animal)harus grup 1 dan (?:=)harus grup 2 dan berlanjut ..

tetapi dengan memberikan ?:kami membuat kelompok pertandingan tidak ditangkap (yang tidak dihitung dalam kelompok cocok, sehingga nomor pengelompokan dimulai dari kelompok yang ditangkap pertama kali dan bukan yang ditangkap), sehingga pengulangan hasil dari kelompok pertandingan (?:animal)tidak dapat dipanggil nanti dalam kode.

Semoga ini menjelaskan penggunaan kelompok yang tidak menangkap.

masukkan deskripsi gambar di sini

shekhar gehlot
sumber
14

Grup yang menangkap Anda dapat menggunakan nanti di regex untuk mencocokkan ATAU Anda dapat menggunakannya di bagian pengganti regex. Membuat grup yang tidak menangkap cukup mengecualikan grup tersebut dari penggunaan karena salah satu dari alasan ini.

Grup yang tidak menangkap sangat bagus jika Anda mencoba untuk menangkap banyak hal yang berbeda dan ada beberapa kelompok yang tidak ingin Anda tangkap.

Itulah alasan mengapa mereka ada. Saat Anda belajar tentang grup, belajar tentang Grup Atom , mereka melakukan banyak hal! Ada juga kelompok lookaround tetapi mereka sedikit lebih kompleks dan tidak banyak digunakan.

Contoh penggunaan nanti dalam regex (backreference):

<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1> [Menemukan tag xml (tanpa dukungan ns)]

([A-Z][A-Z0-9]*) adalah grup penangkap (dalam hal ini adalah tagname)

Kemudian dalam regex adalah \1yang berarti hanya akan cocok dengan teks yang sama yang ada di grup pertama ( ([A-Z][A-Z0-9]*)grup) (dalam hal ini cocok dengan tag akhir).

Bob Fincheimer
sumber
dapatkah Anda memberikan contoh sederhana bagaimana ini akan digunakan nanti untuk mencocokkan ATAU?
never_had_a_name
maksud saya Anda dapat menggunakan untuk mencocokkan nanti atau Anda dapat menggunakannya dalam penggantian. Atau dalam kalimat itu hanya untuk menunjukkan kepada Anda ada dua kegunaan untuk kelompok penangkap
Bob Fincheimer
9

Yah saya adalah pengembang JavaScript dan akan mencoba menjelaskan maknanya yang berkaitan dengan JavaScript.

Pertimbangkan skenario di mana Anda ingin mencocokkan cat is animal ketika Anda ingin mencocokkan kucing dan hewan dan keduanya harus ada isdi antara mereka.

 // 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"]
Gaurav
sumber
7

Dalam ekspresi reguler yang kompleks, Anda mungkin memiliki situasi timbul di mana Anda ingin menggunakan sejumlah besar kelompok beberapa di antaranya ada untuk pencocokan pengulangan dan beberapa di antaranya ada untuk memberikan referensi kembali. Secara default, teks yang cocok dengan setiap grup dimuat ke dalam array referensi-balik. Di mana kami memiliki banyak grup dan hanya perlu dapat merujuk beberapa dari mereka dari array backreference, kami dapat mengganti perilaku default ini untuk memberi tahu ekspresi reguler bahwa grup tertentu hanya ada untuk penanganan pengulangan dan tidak perlu ditangkap dan disimpan dalam array backreference.

Jack Peng
sumber
7

Saya tidak dapat mengomentari jawaban teratas untuk mengatakan ini: Saya ingin menambahkan titik eksplisit yang hanya tersirat dalam jawaban teratas:

Kelompok non-menangkap (?...) tidak tidak menghapus karakter apapun dari pertandingan penuh asli, hanya mereorganisasi regex visual untuk programmer.

Untuk mengakses bagian tertentu dari regex tanpa karakter asing yang ditentukan, Anda harus selalu menggunakannya .group(<index>)

Scott Anderson
sumber
2
Anda telah memberikan petunjuk paling penting yang tidak ada di sisa jawaban. Saya mencoba semua contoh di dalamnya dan menggunakan kata-kata kasar pilihan, karena saya tidak mendapatkan hasil yang diinginkan. Hanya posting Anda yang menunjukkan kesalahan saya.
Seshadri R
Senang mendengarnya!
Scott Anderson
6

tl; dr grup yang tidak menangkap, seperti namanya adalah bagian dari regex yang tidak ingin Anda sertakan dalam pertandingan dan ?:merupakan cara untuk mendefinisikan grup sebagai tidak menangkap.

Katakanlah Anda memiliki alamat email [email protected]. Regex berikut akan membuat dua grup , bagian id dan bagian @ example.com. (\p{Alpha}*[a-z])(@example.com). Demi kesederhanaan, kami mengekstraksi seluruh nama domain termasuk @karakter.

Sekarang katakanlah, Anda hanya perlu bagian id dari alamat. Yang ingin Anda lakukan adalah mengambil grup pertama dari hasil pertandingan, dikelilingi oleh ()dalam regex dan cara untuk melakukannya adalah dengan menggunakan sintaks grup non-capturing, yaitu ?:. Jadi regex (\p{Alpha}*[a-z])(?:@example.com)akan mengembalikan hanya bagian id dari email.

6 pak anak
sumber
5

Satu hal menarik yang saya temui adalah kenyataan bahwa Anda dapat memiliki grup menangkap di dalam grup yang tidak menangkap. Lihat di bawah ini regex untuk mencocokkan url web:

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

Masukkan url string:

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

Grup pertama di regex saya (?:([A-Za-z]+):)adalah grup non-menangkap yang cocok dengan skema protokol dan :karakter titik dua yaitu http:tetapi ketika saya menjalankan kode di bawah, saya melihat indeks pertama dari array yang dikembalikan berisi string httpketika saya memikirkan itu httpdan titik dua :keduanya tidak akan dilaporkan karena mereka berada di dalam kelompok yang tidak menangkap.

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

masukkan deskripsi gambar di sini

Saya pikir jika grup pertama (?:([A-Za-z]+):)adalah grup yang tidak menangkap maka mengapa ia mengembalikan httpstring dalam array output.

Jadi, jika Anda perhatikan ada grup bersarang ([A-Za-z]+)di dalam grup yang tidak menangkap. Grup bersarang itu ([A-Za-z]+)adalah grup yang menangkap (tidak memiliki ?:di awal) di dalam dirinya sendiri di dalam grup yang tidak menangkap (?:([A-Za-z]+):). Itu sebabnya teks httpmasih dapat ditangkap tetapi :karakter titik dua yang ada di dalam grup yang tidak menangkap tetapi di luar grup yang menangkap tidak dilaporkan dalam larik keluaran.

RBT
sumber
2

Buka devTools Google Chrome Anda dan kemudian tab Console: dan ketik ini:

"Peace".match(/(\w)(\w)(\w)/)

Jalankan dan Anda akan melihat:

["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]

Mesin JavaScriptRegExp menangkap tiga grup, item dengan indeks 1,2,3. Sekarang gunakan tanda yang tidak menangkap untuk melihat hasilnya.

"Peace".match(/(?:\w)(\w)(\w)/)

Hasilnya adalah:

["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]

Ini jelas apa yang dimaksud dengan kelompok tidak menangkap.

Amerika
sumber
2

Saya pikir saya akan memberi Anda jawabannya. Jangan gunakan variabel penangkapan tanpa memeriksa apakah pertandingan berhasil.

Variabel capture $1,, dll, tidak valid kecuali jika pertandingan berhasil, dan mereka juga tidak dihapus.

#!/usr/bin/perl  
use warnings;
use strict;   
$_ = "bronto saurus burger";
if (/(?:bronto)? saurus (steak|burger)/)
{
    print "Fred wants a  $1";
}
else
{
    print "Fred dont wants a $1 $2";
}

Dalam contoh di atas, untuk menghindari penangkapan bronto $1, (?:)digunakan.

Jika polanya cocok, maka $1ditangkap sebagai pola yang dikelompokkan berikutnya.

Jadi, hasilnya akan seperti di bawah ini:

Fred wants a burger

Ini berguna jika Anda tidak ingin korek api disimpan.

Harini
sumber
1

Ini sangat sederhana, Kita dapat mengerti dengan contoh tanggal sederhana, misalkan jika tanggal tersebut disebutkan sebagai 1 Januari 2019 atau 2 Mei 2019 atau tanggal lainnya dan kami hanya ingin mengubahnya menjadi format dd / mm / yyyy kita tidak memerlukan bulan nama yang Januari atau Februari dalam hal ini, jadi untuk menangkap bagian numerik, tetapi bukan sufiks (opsional) Anda dapat menggunakan grup yang tidak menangkap.

jadi ekspresi regulernya adalah,

([0-9]+)(?:January|February)?

Sesederhana itu.

Naved Ahmad
sumber