Bagaimana cara mencocokkan, tetapi tidak menangkap, bagian dari regex?

210

Saya punya daftar string. Beberapa dari mereka memiliki bentuk 123-...456. Bagian variabel "..." mungkin:

  • string "apel" diikuti dengan tanda hubung, misalnya 123-apple-456
  • string "pisang" diikuti oleh tanda hubung, misalnya 123-banana-456
  • string kosong, misalnya 123-456(perhatikan hanya ada satu tanda hubung)

Kata apa pun selain "apel" atau "pisang" tidak valid.

Untuk tiga kasus ini, saya ingin mencocokkan masing-masing "apel", "pisang", dan "". Perhatikan bahwa saya tidak pernah ingin menangkap tanda hubung, tetapi saya selalu ingin mencocokkannya . Jika string bukan dari bentuk 123-...456seperti dijelaskan di atas, maka tidak ada kecocokan sama sekali.

Bagaimana cara menulis ekspresi reguler untuk melakukan ini? Anggaplah saya memiliki citarasa yang memungkinkan kelompok yang melihat ke depan, melihat ke belakang, mencari di sekitar, dan tidak menangkap.


Pengamatan utama di sini adalah bahwa ketika Anda memiliki "apel" atau "pisang", Anda juga harus memiliki tanda hubung tambahan, tetapi Anda tidak ingin mencocokkannya. Dan saat Anda mencocokkan string kosong, Anda tidak harus memiliki tanda hubung tambahan. Regex yang merangkum pernyataan ini akan menjadi yang benar, saya pikir.

David Stone
sumber
Anda ingin mencocokkan semuanya kecuali untuk tanda hubung?
BrunoLM

Jawaban:

286

Satu-satunya cara untuk tidak menangkap sesuatu adalah menggunakan pernyataan melihat-lihat :

(?<=123-)((apple|banana)(?=-456)|(?=456))

Karena bahkan dengan grup yang tidak menangkap(?:…) seluruh ekspresi reguler menangkap konten mereka yang cocok. Tetapi ungkapan reguler ini hanya cocok appleatau bananajika itu didahului oleh 123-dan diikuti oleh -456, atau cocok dengan string kosong jika diawali 123-dan diikuti oleh 456.

|Lookaround  |    Name      |        What it Does                       |
-----------------------------------------------------------------------
|(?=foo)     |   Lookahead  | Asserts that what immediately FOLLOWS the |
|            |              |  current position in the string is foo    |
-------------------------------------------------------------------------
|(?<=foo)    |   Lookbehind | Asserts that what immediately PRECEDES the|
|            |              |  current position in the string is foo    |
-------------------------------------------------------------------------
|(?!foo)     |   Negative   | Asserts that what immediately FOLLOWS the |
|            |   Lookahead  |  current position in the string is NOT foo|
-------------------------------------------------------------------------
|(?<!foo)    |   Negative   | Asserts that what immediately PRECEDES the|
|            |   Lookbehind |  current position in the string is NOT foo|
-------------------------------------------------------------------------
Gumbo
sumber
1
+1 - Dalam hal ini, Anda dapat mengatasinya dengan menggunakan grup 1 daripada grup 0, tetapi ini adalah perbedaan yang sangat baik (dan halus!).
Ben Blank
@ Ben Blank: Ini pasti tergantung pada bagaimana "pertandingan" dan "menangkap" ditafsirkan.
Gumbo
8
Tidak didukung dalam JavaScript, yay ! akan menyenangkan untuk memiliki metode JS friendly, tetapi tidak buruk sama sekali, +0,5 (pembulatan; D)
GiantCowFilms
Cinta pernyataan melihat-lihat! Ini berfungsi baik dengan Ruby juga.
Rots
solusi sempurna, saya suka ini
Trần Quang Hiệp
15

Pembaruan: Terima kasih kepada Germán Rodríguez Herrera!

Dalam javascript coba: /123-(apple(?=-)|banana(?=-)|(?!-))-?456/

Ingat bahwa hasilnya ada di grup 1

Demo Debuggex

op1ekun
sumber
8

Mencoba:

123-(?:(apple|banana|)-|)456

Itu akan cocok apple,, bananaatau string kosong, dan mengikutinya akan ada 0 atau 1 tanda hubung. Saya salah karena tidak memiliki kebutuhan untuk menangkap kelompok. Saya konyol.

Thomas
sumber
Ini tidak benar karena cocok, misalnya, "123-coconut-456".
David Stone
Kupikir kau menginginkannya lebih umum ... diperbaiki.
Thomas
5

Saya telah memodifikasi salah satu jawaban (oleh @ op1ekun):

123-(apple(?=-)|banana(?=-)|(?!-))-?456

Alasannya adalah bahwa jawaban dari @ op1ekun juga cocok "123-apple456", tanpa tanda hubung setelah apel.

Germán Rodríguez Herrera
sumber
3

Coba ini:

/\d{3}-(?:(apple|banana)-)?\d{3}/
slosd
sumber
1
Ini tidak benar karena cocok, misalnya, "123-coconut-456".
David Stone
@david: apa bedanya dengan contoh "pisang" Anda?
SilentGhost
@SilentGhost: Saya hanya ingin menangkap appleatau bananaatau "". Semua nilai lain tidak valid, seperti yang saya nyatakan.
David Stone
sry, dalam hal ini: / \ d {3} - (? :( apple | banana) -)? \ d {3} /
slosd
1
Apa yang ditunjukkan contoh ini adalah bahwa dimungkinkan untuk memiliki grup yang tidak menangkap tanpa menggunakan lookahead dan lookbehind.
Vince Panuccio
0

Variasi ekspresi oleh @Gumbo yang digunakan \Kuntuk mengatur ulang posisi pertandingan untuk mencegah dimasukkannya blok angka dalam pertandingan. Dapat digunakan dalam rasa regre PCRE.

123-\K(?:(?:apple|banana)(?=-456)|456\K)

Cocok:

Match 1  apple
Match 2  banana
Match 3
oriberu
sumber
-3

Sejauh ini yang paling sederhana (berfungsi untuk python) adalah '123-(apple|banana)-?456'.

johmsp
sumber
1
Ini akan cocok 123-apple456sehingga tidak benar.
Loren