Hans, yemi alacağım ve önceki cevabımı çözeceğim. "Daha eksiksiz bir şey" istediğinizi söylediniz, bu yüzden umarım uzun cevabı aldırmazsınız - sadece memnun etmeye çalışıyorum. Biraz arka planla başlayalım.
Öncelikle, bu mükemmel bir soru. Belirli bağlamlar dışında (örneğin, bir kod bloğu içinde veya parantez içinde) belirli kalıpları eşleştirmeyle ilgili sıklıkla sorular vardır. Bu sorular genellikle oldukça garip çözümlere yol açar. Dolayısıyla, çoklu bağlamlarla ilgili sorunuz özel bir sorundur.
Sürpriz
Şaşırtıcı bir şekilde, genel, uygulaması kolay ve bakımı zevkli olan en az bir etkili çözüm var. O tüm regex lezzetleri ile çalışır kodunuzda yakalama grupları incelemek için izin verir. Ve ilk başta sizinkinden farklı olabilecek bir dizi yaygın soruyu yanıtlıyor: "Donutlar hariç her şeyi eşleştir", "... hariç hepsini değiştir", "annemin kara listesindekiler dışındaki tüm kelimeleri eşleştir", "yok say etiketleri "," italik değilse sıcaklıkla eşleşir "...
Ne yazık ki, teknik iyi bilinmemektedir: Onu kullanabilecek yirmi SO sorusunda sadece birinin ondan bahseden bir cevabı olduğunu tahmin ediyorum - bu da belki elli veya altmış cevaptan biri anlamına geliyor. Yorumlarda Kobi ile değişimimi görün. Teknik, onu (iyimser bir şekilde) "şimdiye kadarki en iyi düzenli ifade numarası" olarak adlandıran bu makalede biraz derinlemesine açıklanmıştır . Bu kadar detaya girmeden, tekniğin nasıl çalıştığını size sağlam bir şekilde anlamaya çalışacağım. Çeşitli dillerde daha fazla ayrıntı ve kod örnekleri için bu kaynağa başvurmanızı öneririm.
Daha İyi Bilinen Bir Varyasyon
Perl ve PHP'ye özgü sözdizimini kullanan ve aynı şeyi yapan bir varyasyon var. Bunu, CasimiretHippolyte ve HamZa gibi regex ustalarının elinde SO'da göreceksiniz . Aşağıda size bununla ilgili daha fazla bilgi vereceğim, ancak buradaki odak noktam, tüm normal ifade çeşitleriyle çalışan genel çözümdür (kodunuzdaki yakalama gruplarını inceleyebildiğiniz sürece).
Tüm arka plan için teşekkürler, zx81 ... Ama tarif nedir?
Anahtar Gerçek
Yöntem, 1. Grup yakalamasındaki eşleşmeyi döndürür. Genel maç hiç umurunda değil.
Aslında, işin püf noktası, istemediğimiz çeşitli bağlamları eşleştirmek (bu bağlamları |
OR / dönüşüm kullanarak zincirleme ) böylece "onları etkisiz hale getirmektir". Tüm istenmeyen bağlamlar eşleşen sonra, münavebe son bölümü biz eşleşir mi istiyor ve Grup 1'e yakalar bunu.
Genel tarif
Not_this_context|Not_this_either|StayAway|(WhatYouWant)
Bu eşleşecek Not_this_context
, ancak bir anlamda bu eşleşme çöp kutusuna gidiyor, çünkü genel eşleşmelere bakmayacağız: sadece Grup 1 yakalamalarına bakıyoruz.
Sizin durumunuzda, rakamlarınız ve görmezden gelinecek üç bağlamınız ile şunları yapabiliriz:
s1|s2|s3|(\b\d+\b)
Aslında s1, s2 ve s3 ile eşleştiğimiz için, bunlardan kaçınmaya çalışmak yerine, s1, s2 ve s3 için tek tek ifadelerin gün kadar net kalabileceğini unutmayın. (A'nın her iki yanındaki alt ifadelerdir |
)
Tüm ifade şu şekilde yazılabilir:
(?m)^.*\.$|\([^\)]*\)|if\(.*?//endif|(\b\d+\b)
Bu demoyu izleyin (ancak sağ alt bölmedeki yakalama gruplarına odaklanın.)
Bu normal |
ifadeyi her bir sınırlayıcıda zihinsel olarak bölmeye çalışırsanız , bu aslında yalnızca dört çok basit ifadeden oluşan bir dizidir.
Serbest aralığı destekleyen tatlar için bu özellikle iyi okur.
(?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)
Bunun okunması ve bakımı son derece kolaydır.
Normal ifadeyi genişletme
Daha fazla s4 ve s5 durumunu göz ardı etmek istediğinizde, bunları sol tarafa daha fazla alternatif olarak eklersiniz:
s4|s5|s1|s2|s3|(\b\d+\b)
Bu nasıl çalışıyor?
İstemediğiniz bağlamlar soldaki alternatifler listesine eklenir: eşleşirler, ancak bu genel eşleşmeler hiçbir zaman incelenmez, bu nedenle bunları eşleştirmek onları bir "çöp kutusuna" koymanın bir yoludur.
Bununla birlikte, istediğiniz içerik Grup 1'e yakalanır. Ardından, Programlı olarak Grup 1'in boş olmadığını ve Grup 1'in ayarlanmış olduğunu kontrol etmeniz gerekir. Bu önemsiz bir programlama görevidir (ve daha sonra nasıl yapıldığından bahsedeceğiz), özellikle de size bir bakışta anlayabileceğiniz ve gerektiği gibi gözden geçirebileceğiniz veya genişletebileceğiniz basit bir normal ifade bıraktığını düşünürsek.
Her zaman görselleştirme hayranı değilim, ancak bu, yöntemin ne kadar basit olduğunu gösterme konusunda iyi bir iş çıkarıyor. Her "satır" potansiyel bir eşleşmeye karşılık gelir, ancak yalnızca alt satır Grup 1'e alınır.
Debuggex Demosu
Perl / PCRE Varyasyonu
Yukarıdaki genel çözümün aksine, en azından @CasimiretHippolyte ve @HamZa gibi normal ifade Tanrılarının elinde, SO'da sıklıkla görülen Perl ve PCRE için bir varyasyon vardır. Bu:
(?:s1|s2|s3)(*SKIP)(*F)|whatYouWant
Senin durumunda:
(?m)(?:^.*\.$|\([^()]*\)|if\(.*?//endif)(*SKIP)(*F)|\b\d+\b
Bu varyasyonun kullanımı biraz daha kolaydır çünkü s1, s2 ve s3 bağlamlarında eşleşen içerik basitçe atlanır, bu nedenle Grup 1 yakalamalarını incelemeniz gerekmez (parantezlerin kaybolduğuna dikkat edin). Maçlar sadece şunları içerir:whatYouWant
Not (*F)
, (*FAIL)
ve (?!)
hepsi aynı şeydir. Daha belirsiz olmak istersen, kullanabilirsin(*SKIP)(?!)
demo Bu sürüm için
Uygulamalar
İşte bu tekniğin genellikle kolayca çözebileceği bazı yaygın sorunlar. Seçme kelimesinin bu problemlerden bazılarının kulağa farklı gelmesine rağmen aslında aynı olduklarını fark edeceksiniz.
- Gibi bir etiketin herhangi bir yeri dışında foo'yu nasıl eşleştirebilirim
<a stuff...>...</a>
?
- Bir
<i>
etiket veya bir JavaScript pasajı dışında foo'yu nasıl eşleştirebilirim (daha fazla koşul)?
- Bu kara listede olmayan tüm kelimeleri nasıl eşleştirebilirim?
- Bir SUB ... END SUB bloğu içindeki herhangi bir şeyi nasıl göz ardı edebilirim?
- ... s1 s2 s3 dışındaki her şeyi nasıl eşleştirebilirim?
1. Grup Yakalamaları Nasıl Programlanır
Kod istemiyordunuz, ama tamamlanması için ... Grup 1'i inceleyecek kod, açıkça seçtiğiniz dile bağlı olacaktır. Her halükarda, eşleşmeleri incelemek için kullanacağınız koda birkaç satırdan fazlasını eklememelidir.
Şüpheniz varsa, daha önce bahsedilen makalenin birkaç dil için kod sunan kod örnekleri bölümüne bakmanızı tavsiye ederim .
Alternatifler
Sorunun karmaşıklığına ve kullanılan normal ifade motoruna bağlı olarak birkaç alternatif vardır. Birden çok koşul dahil olmak üzere çoğu durum için geçerli olabilecek ikisi aşağıda verilmiştir. Benim görüşüme göre, s1|s2|s3|(whatYouWant)
sadece netlik her zaman kazanırsa, her ikisi de tarif kadar çekici değildir .
1. Değiştirin ve ardından Eşleştirin.
Keskin görünen ancak birçok ortamda iyi çalışan iyi bir çözüm, iki adımda çalışmaktır. İlk normal ifade, potansiyel olarak çakışan dizeleri değiştirerek yok saymak istediğiniz bağlamı etkisiz hale getirir. Yalnızca eşleştirmek istiyorsanız, boş bir dizeyle değiştirebilir, ardından ikinci adımda eşleşmenizi çalıştırabilirsiniz. Değiştirmek istiyorsanız, önce yok sayılacak dizeleri farklı bir şeyle değiştirebilirsiniz, örneğin rakamlarınızı sabit genişlikte bir zincir ile çevrelemek @@@
. Bu değiştirmeden sonra, gerçekten istediğiniz şeyi değiştirmekte özgürsünüz, ardından ayırt edici @@@
dizelerinizi geri almanız gerekecek .
2. Bakışlar.
Orijinal gönderiniz, bakma çözümleri kullanarak tek bir koşulu nasıl hariç tutacağınızı anladığınızı gösterdi. C # 'ın bunun için harika olduğunu söylediniz ve haklısınız, ancak tek seçenek bu değil. Örneğin C #, VB.NET ve Visual C ++ 'da bulunan .NET regex tatları ve Python'da regex
değiştirilecek hala deneysel modül re
, sonsuz genişlikte bakmayı destekleyen bildiğim tek iki motordur. Bu araçlarla, bir bakışta bir koşul, sadece arkaya değil, aynı zamanda maça ve maçın ötesine de bakabilir ve bir bakışla koordine etme ihtiyacını ortadan kaldırabilir. Daha fazla koşul mu? Daha fazla bakış.
C # 'da s3 için sahip olduğunuz regex'i geri dönüştürmek, tüm kalıp şöyle görünecektir.
(?!.*\.)(?<!\([^()]*(?=\d+[^)]*\)))(?<!if\(\D*(?=\d+.*?//endif))\b\d+\b
Ama şimdiye kadar bunu önermediğimi biliyorsun, değil mi?
Silmeler
@HamZa ve @Jerry, sadece silmek istediğiniz durumlar için ek bir numaradan bahsetmemi önerdi WhatYouWant
. Eşleşecek tarifin WhatYouWant
(onu Grup 1'e çekerek) olduğunu hatırlıyorsunuz s1|s2|s3|(WhatYouWant)
, değil mi? Tüm örneğini silmek WhatYouWant
için normal ifadeyi şu şekilde değiştirirsiniz:
(s1|s2|s3)|WhatYouWant
Değiştirme dizesi için kullanırsınız $1
. Burada olan s1|s2|s3
şey, eşleştirilen her bir örnek için , $1
değiştirmenin bu örneği kendisiyle değiştirmesidir (tarafından başvurulur $1
). Öte yandan, WhatYouWant
eşleştirildiğinde, boş bir grupla değiştirilir ve başka hiçbir şeyle değiştirilmez - ve bu nedenle silinir. Bu demoyu izleyin, bu harika eklemeyi önerdiğiniz için @HamZa ve @ Jerry'ye teşekkür ederiz.
Değiştirmeler
Bu bizi kısaca değineceğim değiştirmelere getiriyor.
- Hiçbir şeyle değiştirirken yukarıdaki "Silinmeler" numarasına bakın.
- Değiştirirken, Perl veya PCRE kullanılıyorsa,
(*SKIP)(*F)
tam olarak istediğinizi eşleştirmek için yukarıda belirtilen varyasyonu kullanın ve doğrudan bir değiştirme yapın.
- Diğer tatlarda, değiştirme işlevi çağrısı içinde, bir geri arama veya lambda kullanarak eşleşmeyi inceleyin ve Grup 1 ayarlanmışsa değiştirin. Bununla ilgili yardıma ihtiyacınız varsa, daha önce atıfta bulunulan makale size çeşitli dillerde kod verecektir.
İyi eğlenceler!
Hayır, bekle, dahası var!
Ah, nah, bunu önümüzdeki ilkbaharda yayınlanacak yirmi ciltlik anılarım için saklayacağım.
\K
özel bir php sözdizimi değildir. Lütfen ne söylemek istediğinizi detaylandırın ve netleştirin. Bize "karmaşık" bir çözüme ihtiyacınız olmadığını söylemeyi hedefliyorsanız, sizin için neyin karmaşık olduğunu ve neden olduğunu söylemeniz gerekir.