Hans, saya akan mengambil umpan dan menyempurnakan jawaban saya sebelumnya. Anda bilang ingin "sesuatu yang lebih lengkap" jadi saya harap Anda tidak keberatan dengan jawaban yang panjang — hanya mencoba menyenangkan. Mari kita mulai dengan beberapa latar belakang.
Pertama, ini adalah pertanyaan yang bagus. Sering ada pertanyaan tentang mencocokkan pola tertentu kecuali dalam konteks tertentu (misalnya, dalam blok kode atau di dalam tanda kurung). Pertanyaan-pertanyaan ini sering kali menimbulkan solusi yang cukup canggung. Jadi pertanyaan Anda tentang berbagai konteks merupakan tantangan khusus.
Mengherankan
Anehnya, setidaknya ada satu solusi efisien yang umum, mudah diterapkan, dan menyenangkan untuk dipelihara. Ia bekerja dengan semua ragam regex yang memungkinkan Anda untuk memeriksa grup tangkapan dalam kode Anda. Dan itu kebetulan menjawab sejumlah pertanyaan umum yang mungkin pada awalnya terdengar berbeda dari pertanyaan Anda: "cocokkan semuanya kecuali Donat", "ganti semua kecuali ...", "cocokkan semua kata kecuali yang ada di daftar hitam ibu saya", "abaikan tag "," cocok dengan suhu kecuali dicetak miring "...
Sayangnya, teknik ini tidak begitu dikenal: Saya memperkirakan bahwa dalam dua puluh pertanyaan SO yang dapat menggunakannya, hanya satu yang memiliki satu jawaban yang menyebutkannya — yang berarti mungkin satu dari lima puluh atau enam puluh jawaban. Lihat pertukaran saya dengan Kobi di komentar. Teknik ini dijelaskan secara mendalam dalam artikel ini yang menyebutnya (secara optimis) sebagai "trik regex terbaik yang pernah ada". Tanpa membahas terlalu detail, saya akan mencoba memberi Anda pemahaman yang kuat tentang cara kerja teknik ini. Untuk detail lebih lanjut dan contoh kode dalam berbagai bahasa, saya mendorong Anda untuk melihat sumber daya itu.
Variasi yang Lebih Diketahui
Ada variasi menggunakan sintaks khusus untuk Perl dan PHP yang melakukan hal yang sama. Anda akan melihatnya di SO di tangan master regex seperti CasimiretHippolyte dan HamZa . Saya akan memberi tahu Anda lebih banyak tentang ini di bawah, tetapi fokus saya di sini adalah pada solusi umum yang berfungsi dengan semua rasa regex (selama Anda dapat memeriksa grup tangkapan dalam kode Anda).
Terima kasih untuk semua latar belakangnya, zx81 ... Tapi apa resepnya?
Fakta Kunci
Metode ini mengembalikan kecocokan dalam penangkapan Grup 1. Itu sama sekali tidak peduli tentang pertandingan secara keseluruhan.
Faktanya, triknya adalah mencocokkan berbagai konteks yang tidak kita inginkan (merangkai konteks ini menggunakan |
OR / alternasi) untuk "menetralkannya". Setelah pencocokan semua konteks yang tidak diinginkan, bagian akhir dari pergantian cocok apa yang kita lakukan inginkan dan menangkap ke Grup 1.
Resep umumnya adalah
Not_this_context|Not_this_either|StayAway|(WhatYouWant)
Ini akan cocok Not_this_context
, tetapi dalam arti bahwa pertandingan tersebut masuk ke tempat sampah, karena kita tidak akan melihat pertandingan secara keseluruhan: kita hanya melihat tangkapan Grup 1.
Dalam kasus Anda, dengan angka Anda dan tiga konteks Anda untuk diabaikan, kami dapat melakukan:
s1|s2|s3|(\b\d+\b)
Perhatikan bahwa karena kami benar-benar mencocokkan s1, s2, dan s3 daripada mencoba menghindarinya dengan lookarounds, ekspresi individu untuk s1, s2 dan s3 dapat tetap jelas sebagai hari. (Mereka adalah subekspresi di setiap sisi a |
)
Seluruh ekspresi bisa ditulis seperti ini:
(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)
Lihat demo ini (tapi fokus pada grup pengambilan di panel kanan bawah.)
Jika Anda secara mental mencoba membagi regex ini di setiap |
pembatas, sebenarnya itu hanya rangkaian empat ekspresi yang sangat sederhana.
Untuk rasa yang mendukung spasi bebas, bacaan ini sangat baik.
(?mx)
### s1: Match line that ends with a period ###
^.*\.$
| ### OR s2: Match anything between parentheses ###
\([^\)]*\)
| ### OR s3: Match any if(...//endif block ###
if\(.*?//endif
| ### OR capture digits to Group 1 ###
(\b\d+\b)
Ini sangat mudah dibaca dan dipelihara.
Memperluas regex
Saat Anda ingin mengabaikan lebih banyak situasi s4 dan s5, Anda menambahkannya di lebih banyak alternatif di sebelah kiri:
s4|s5|s1|s2|s3|(\b\d+\b)
Bagaimana cara kerjanya?
Konteks yang tidak Anda inginkan ditambahkan ke daftar alternatif di sebelah kiri: mereka akan cocok, tetapi kecocokan keseluruhan ini tidak pernah diperiksa, jadi mencocokkannya adalah cara untuk meletakkannya di "tempat sampah".
Konten yang Anda inginkan, bagaimanapun, disimpan ke Grup 1. Anda kemudian harus memeriksa secara terprogram bahwa Grup 1 telah disetel dan tidak kosong. Ini adalah tugas pemrograman yang sepele (dan nanti kita akan membicarakan cara melakukannya), terutama mengingat hal itu membuat Anda memiliki regex sederhana yang dapat Anda pahami secara sekilas dan merevisi atau memperluas sesuai kebutuhan.
Saya tidak selalu menyukai visualisasi, tapi yang satu ini menunjukkan betapa sederhananya metodenya. Setiap "baris" sesuai dengan pertandingan potensial, tetapi hanya keuntungan yang diambil ke dalam Grup 1.
Demo Debuggex
Variasi Perl / PCRE
Berbeda dengan solusi umum di atas, terdapat variasi Perl dan PCRE yang sering terlihat di SO, setidaknya di tangan Dewa regex seperti @CasimiretHippolyte dan @HamZa. Ini:
(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant
Dalam kasus Anda:
(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b
Variasi ini sedikit lebih mudah digunakan karena konten yang cocok dengan konteks s1, s2 dan s3 dilewati begitu saja, jadi Anda tidak perlu memeriksa tangkapan Grup 1 (perhatikan tanda kurung tidak ada). Pertandingan hanya berisiwhatYouWant
Perhatikan bahwa (*F)
, (*FAIL)
dan (?!)
semuanya sama. Jika Anda ingin lebih kabur, Anda bisa menggunakan(*SKIP)(?!)
demo untuk versi ini
Aplikasi
Berikut adalah beberapa masalah umum yang seringkali dapat diselesaikan dengan mudah oleh teknik ini. Anda akan melihat bahwa pilihan kata dapat membuat beberapa masalah ini terdengar berbeda padahal sebenarnya mereka hampir identik.
- Bagaimana saya bisa mencocokkan foo kecuali di mana saja di tag seperti
<a stuff...>...</a>
?
- Bagaimana cara mencocokkan foo kecuali dalam
<i>
tag atau cuplikan javascript (ketentuan lainnya)?
- Bagaimana saya bisa mencocokkan semua kata yang tidak ada di daftar hitam ini?
- Bagaimana saya bisa mengabaikan apapun di dalam blok SUB ... END SUB?
- Bagaimana saya bisa mencocokkan semuanya kecuali ... s1 s2 s3?
Bagaimana Memprogram Tangkapan Grup 1
Anda tidak memberikan kode, tetapi, untuk penyelesaiannya ... Kode untuk memeriksa Grup 1 jelas akan bergantung pada bahasa pilihan Anda. Bagaimanapun itu tidak boleh menambahkan lebih dari beberapa baris ke kode yang akan Anda gunakan untuk memeriksa kecocokan.
Jika ragu, saya sarankan Anda melihat bagian contoh kode dari artikel yang disebutkan sebelumnya, yang menyajikan kode untuk beberapa bahasa.
Alternatif
Bergantung pada kompleksitas pertanyaan, dan pada mesin regex yang digunakan, ada beberapa alternatif. Berikut ini dua hal yang dapat diterapkan pada sebagian besar situasi, termasuk beberapa ketentuan. Dalam pandangan saya, tidak ada yang semenarik s1|s2|s3|(whatYouWant)
resepnya, jika hanya karena kejelasan selalu menang.
1. Ganti lalu Cocokkan.
Solusi bagus yang terdengar meretas tetapi berfungsi dengan baik di banyak lingkungan adalah bekerja dalam dua langkah. Regex pertama menetralkan konteks yang ingin Anda abaikan dengan mengganti string yang berpotensi konflik. Jika Anda hanya ingin mencocokkan, maka Anda dapat menggantinya dengan string kosong, lalu jalankan kecocokan Anda di langkah kedua. Jika Anda ingin mengganti, Anda dapat mengganti string diabaikan dengan sesuatu yang berbeda, misalnya mengelilingi digit Anda dengan rantai lebar tetap @@@
. Setelah penggantian ini, Anda bebas mengganti apa yang sebenarnya Anda inginkan, lalu Anda harus mengembalikan @@@
string khusus Anda .
2. Pengamatan.
Postingan asli Anda menunjukkan bahwa Anda memahami cara mengecualikan satu ketentuan menggunakan lookarounds. Anda mengatakan bahwa C # bagus untuk ini, dan Anda benar, tetapi itu bukan satu-satunya pilihan. Rasa .NET regex yang ditemukan di C #, VB.NET dan Visual C ++ misalnya, serta regex
modul yang masih eksperimental untuk diganti re
dengan Python, adalah satu-satunya dua mesin yang saya tahu yang mendukung tampilan lebar tak terbatas. Dengan alat ini, satu syarat dalam satu tampilan di belakang dapat menjaga tidak hanya melihat ke belakang tetapi juga pada pertandingan dan di luar pertandingan, menghindari kebutuhan untuk berkoordinasi dengan seorang lookahead. Lebih banyak kondisi? Lebih banyak pencarian.
Mendaur ulang regex yang Anda miliki untuk s3 di C #, keseluruhan pola akan terlihat seperti ini.
(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
Tapi sekarang Anda tahu saya tidak merekomendasikan ini, bukan?
Penghapusan
@HamZa dan @Jerry menyarankan agar saya menyebutkan trik tambahan untuk kasus-kasus ketika Anda hanya ingin menghapus WhatYouWant
. Anda ingat bahwa resep yang cocok WhatYouWant
(memasukkannya ke dalam Grup 1) adalah s1|s2|s3|(WhatYouWant)
, bukan? Untuk menghapus semua instance WhatYouWant
, Anda mengubah regex menjadi
(s1|s2|s3)|WhatYouWant
Untuk string pengganti, Anda menggunakan $1
. Apa yang terjadi di sini adalah untuk setiap instance s1|s2|s3
yang cocok, penggantian $1
mengganti instance itu dengan dirinya sendiri (direferensikan oleh $1
). Di sisi lain, ketika WhatYouWant
cocok, itu diganti dengan grup kosong dan tidak ada yang lain - dan karena itu dihapus. Lihat demo ini , terima kasih @HamZa dan @Jerry karena menyarankan tambahan yang luar biasa ini.
Pengganti
Ini membawa kita ke penggantinya, yang akan saya sentuh sebentar.
- Saat mengganti dengan apa-apa, lihat trik "Penghapusan" di atas.
- Saat mengganti, jika menggunakan Perl atau PCRE, gunakan
(*SKIP)(*F)
variasi yang disebutkan di atas agar sesuai dengan yang Anda inginkan, dan lakukan penggantian langsung.
- Dalam ragam lain, dalam pemanggilan fungsi pengganti, periksa kecocokan menggunakan callback atau lambda, dan ganti jika Grup 1 disetel. Jika Anda memerlukan bantuan untuk ini, artikel yang sudah direferensikan akan memberi Anda kode dalam berbagai bahasa.
Selamat bersenang-senang!
Tidak, tunggu, masih ada lagi!
Ah, nah, aku akan menyimpannya untuk memoarku dalam dua puluh volume, yang akan dirilis Musim Semi mendatang.
\K
tidak ada sintaks php khusus. Harap uraikan dan klarifikasi apa yang ingin Anda katakan. Jika Anda bertujuan memberi tahu kami bahwa Anda tidak memerlukan solusi yang "rumit", Anda harus mengatakan apa yang rumit untuk Anda dan mengapa.