Yakalamayan gruplar, yani (?:)
düzenli ifadelerde nasıl kullanılır ve ne işe yarar?
Yakalamayan gruplar, yani (?:)
düzenli ifadelerde nasıl kullanılır ve ne işe yarar?
Yanıtlar:
Bunu bir örnekle açıklamaya çalışayım.
Aşağıdaki metni düşünün:
http://stackoverflow.com/
/programming/tagged/regex
Şimdi, aşağıdaki normal ifadeyi üzerine uygularsam ...
(https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
... şu sonucu elde ederim:
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"
Ama protokol umurumda değil - Ben sadece URL host ve yol istiyorum. Bu nedenle, regex'i yakalamayan grubu içerecek şekilde değiştiriyorum (?:)
.
(?:https?|ftp)://([^/\r\n]+)(/[^\r\n]*)?
Şimdi, sonucum şöyle:
Match "http://stackoverflow.com/"
Group 1: "stackoverflow.com"
Group 2: "/"
Match "/programming/tagged/regex"
Group 1: "stackoverflow.com"
Group 2: "/questions/tagged/regex"
Görmek? İlk grup yakalanmadı. Ayrıştırıcı metni metne uydurmak için kullanır ancak nihai sonuçta daha sonra yok sayar.
İstendiği gibi, grupları da açıklamaya çalışayım.
Gruplar birçok amaca hizmet ediyor. Daha büyük bir maçtan (aynı zamanda adlandırılabilir) kesin bilgileri çıkarmanıza yardımcı olabilirler, daha önce eşleşen bir grubu yeniden oluşturmanıza izin verir ve oyuncu değişikliği için kullanılabilirler. Bazı örnekler deneyelim, olur mu?
Bir çeşit XML veya HTML'niz olduğunu düşünün ( normal ifadenin iş için en iyi araç olmayabileceğini unutmayın , ancak örnek olarak iyi). Etiketleri ayrıştırmak istiyorsunuz, böylece böyle bir şey yapabilirsiniz (anlaşılmasını kolaylaştırmak için boşluklar ekledim):
\<(?<TAG>.+?)\> [^<]*? \</\k<TAG>\>
or
\<(.+?)\> [^<]*? \</\1\>
İlk regex'in adlandırılmış bir grubu (TAG), ikincisinde ortak bir grup kullanılır. Her iki normal ifade de aynı şeyi yapar: ilk gruptaki değeri (etiketin adı) kapanış etiketiyle eşleştirmek için kullanırlar. Fark, birincinin değeri eşleştirmek için adı kullanması ve ikincisinin (1'den başlayan) grup dizinini kullanmasıdır.
Şimdi bazı ikameleri deneyelim. Aşağıdaki metni düşünün:
Lorem ipsum dolor sit amet consectetuer feugiat fames malesuada pretium egestas.
Şimdi, bu aptal düzenli ifadeyi kullanalım:
\b(\S)(\S)(\S)(\S*)\b
Bu normal ifade en az 3 karakterli kelimelerle eşleşir ve ilk üç harfi ayırmak için grupları kullanır. Sonuç şudur:
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"
...
Yani, ikame dizesini uygularsak:
$1_$3$2_$4
... bunun üzerine, ilk grubu kullanmaya, bir alt çizgi eklemeye, üçüncü grubu, sonra ikinci grubu kullanmaya, başka bir alt çizgi ve sonra dördüncü grubu kullanmaya çalışıyoruz. Ortaya çıkan dize aşağıdaki gibi olacaktır.
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.
Adlı grupları, yerine koymak için de kullanabilirsiniz ${name}
.
Normal ifadelerle oynamak için , normal ifadenin nasıl çalıştığı hakkında ayrıntılı bilgi sunan http://regex101.com/ adresini öneriyorum ; ayrıca aralarından seçim yapabileceğiniz birkaç normal regex motoru sunar.
Bir ifadeyi düzenlemek ve ayrıştırmak için yakalama gruplarını kullanabilirsiniz. Yakalamayan bir grubun ilk yararı vardır, ancak ikincisinin ek yükü yoktur. Örneğin, yakalama yapmayan bir grubun isteğe bağlı olduğunu söyleyebilirsiniz.
Sayısal metni eşleştirmek istediğinizi varsayalım, ancak bazı sayılar 1., 2., 3., 4., ... olarak yazılabilir. Sayısal parçayı yakalamak istiyorsanız (isteğe bağlı) sonek değil, yakalamayan bir grup kullanabilirsiniz .
([0-9]+)(?:st|nd|rd|th)?
Bu, 1, 2, 3 ... veya 1., 2., 3., ... biçimindeki sayılarla eşleşir, ancak yalnızca sayısal kısmı yakalar.
?:
ifadeyi gruplandırmak istediğinizde kullanılır, ancak ifadeyi dizenin eşleşen / yakalanan kısmı olarak kaydetmek istemezsiniz.
Bir örnek, bir IP adresiyle eşleşecek bir şey olabilir:
/(?:\d{1,3}\.){3}\d{1,3}/
İlk 3 okteti kaydetmeyi umursamadığımı, ancak (?:...)
gruplandırma, bir maçı yakalama ve saklama yükünü ödemeden normal ifadeyi kısaltmamı sağlıyor.
Grubu yakalama yapmaz hale getirir, yani o grupla eşleşen alt dize yakalama listesine dahil edilmez. Farkı göstermek için yakutta bir örnek:
"abc".match(/(.)(.)./).captures #=> ["a","b"]
"abc".match(/(?:.)(.)./).captures #=> ["b"]
(?:)
bir yakalama üretmediğini göstermek, yararlı bir örnek göstermek değil (?:)
. (?:)
bir alt ifadeyi gruplamak istediğinizde yararlıdır (atomik olmayan bir alt ifadeye nicelleştiriciler uygulamak istediğinizde veya a'nın kapsamını kısıtlamak istediğinizde diyelim |
), ancak hiçbir şey yakalamak istemezsiniz.
TARİHİ MOTİVASYON:
Yakalamayan grupların varlığı parantez kullanılarak açıklanabilir.
İfadeleri düşünün (a|b)c
ve a|bc
bağlı birleştirme üzeri önceliğine, |
bu ifadelerin iki farklı dilleri temsil ( {ac, bc}
ve{a, bc}
sırasıyla).
Bununla birlikte, parantez de eşleşen grup olarak kullanılır (diğer cevaplarda açıklandığı gibi ...).
Parantez almak ancak alt ifadeyi yakalamak istemiyorsanız, YAKALAMAYAN GRUPLAR kullanırsınız. Örnekte,(?:a|b)c
Bunu bir örnekle deneyeyim:
Normal İfade Kodu: (?:animal)(?:=)(\w+)(,)\1\2
Arama dizisi:
Satır 1 - animal=cat,dog,cat,tiger,dog
Hat 2 - animal=cat,cat,dog,dog,tiger
Satır 3 - animal=dog,dog,cat,cat,tiger
(?:animal)
-> Ele Geçirilmemiş Grup 1
(?:=)
-> Ele Geçirilmemiş Grup 2
(\w+)
-> Yakalanan Grup 1
(,)
-> Yakalanan Grup 2
\1
-> yakalanan grup 1'in sonucu, yani Satır 1'de kedi, Satır 2'de kedi, Satır 3'te köpek.
\2
-> yakalanan grup 2 sonucu virgül (,)
Yani vererek bu kodda \1
ve \2
biz hatırlamak veya kod sırasıyla sonradan yakalanan grup 1 ve 2 sonucunu tekrarlayın.
Kod sırasına göre (?:animal)
grup 1 (?:=)
olmalı ve grup 2 olmalı ve devam etmelidir ..
ancak ?:
eşleşme grubunun yakalanmadığını (eşleşen grupta sayılmadığından, gruplama numarası yakalanandan değil ilk yakalanan gruptan başlar) vererek eşleşme grubu sonucunun tekrarı (?:animal)
daha sonra kodda çağrılamaz.
Umarım bu yakalamayan grubun kullanımını açıklar.
Sizi yakalayan gruplar daha sonra regex'te eşleştirmek için kullanabilir VEYA bunları regex'in yedek bölümünde kullanabilirsiniz. Bir Making olmayan yakalama grubu sadece bu nedenlerden dolayı kullanılan o grubu muaf tutmaktadır.
Çok farklı şeyler yakalamaya çalışıyorsanız ve yakalamak istemediğiniz bazı gruplar varsa yakalamayan gruplar harikadır.
Var olmalarının nedeni bu. Grupları öğrenirken Atom Grupları hakkında bilgi edinin , çok şey yapıyorlar! Ayrıca arama grupları da var, ancak biraz daha karmaşık ve çok fazla kullanılmıyorlar.
Daha sonra normal ifadede (backreference) kullanma örneği:
<([A-Z][A-Z0-9]*)\b[^>]*>.*?</\1>
[Bir xml etiketi bulur (ns desteği olmadan)]
([A-Z][A-Z0-9]*)
bir yakalama grubudur (bu durumda tagname'dir)
Daha sonra normal ifadede \1
bu, yalnızca ilk gruptaki ( ([A-Z][A-Z0-9]*)
grup) metinle eşleşeceği anlamına gelir (bu durumda bitiş etiketiyle eşleşir ).
Ben bir JavaScript geliştiricisiyim ve JavaScript ile ilgili önemini açıklamaya çalışacağım.
cat is animal
Kedi ve hayvanı eşleştirmek istediğinizde eşleştirmek istediğiniz bir senaryo düşünün ve her ikisinin de aralarında bir tane olması gerekir is
.
// 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"]
Karmaşık düzenli ifadelerde, bazıları tekrar eşleştirme için ve bazıları geri referans sağlamak için orada olan çok sayıda grubu kullanmak istediğinizde ortaya çıkabilirsiniz. Varsayılan olarak, her bir grupla eşleşen metin backreference dizisine yüklenir. Çok sayıda grubumuz olduğunda ve bunlardan sadece bazılarını backreference dizisinden referans alabilmemiz gerektiğinde, belirli ifadelerin belirli grupların yalnızca tekrar işleme için olduğunu ve yakalanması ve saklanması gerekmediğini normal ifadeye söylemek için bu varsayılan davranışı geçersiz kılabiliriz. geri başvuru dizisinde.
Bunu söylemek için en iyi cevapları yorum yapamam: Ben sadece en iyi cevapları ima açık bir nokta eklemek istiyorum:
Olmayan yakalama grup (?...)
mu kaldırmaz , orijinal tam maçından herhangi bir karakter sadece programcı görsel normal ifade yeniden yapılandırmaktadır.
Normal olmayan karakterleri tanımlamaksızın normal ifadenin belirli bir bölümüne erişmek için her zaman kullanmanız gerekir .group(<index>)
tl; dr yakalamayan gruplar, adından da anlaşılacağı gibi normal ifadenin maça dahil edilmesini istemediğiniz kısımlarıdır ve ?:
bir grubu yakalama olmayan olarak tanımlamanın bir yoludur.
Diyelim ki bir e-posta adresiniz var example@example.com
. Aşağıdaki normal ifade id bölümü ve @ example.com bölümü olmak üzere iki grup oluşturacaktır . (\p{Alpha}*[a-z])(@example.com)
. Basitlik adına, @
karakter de dahil olmak üzere tüm alan adını çıkarıyoruz.
Şimdi diyelim ki adresin sadece kimlik kısmına ihtiyacınız var. Yapmak istediğiniz şey ()
normal sonuçlarla çevrili maç sonucunun ilk grubunu almaktır ve bunu yapmanın yolu yakalamayan grup sözdizimini kullanmaktır ?:
. Böylece normal (\p{Alpha}*[a-z])(?:@example.com)
ifade, e-postanın yalnızca kimlik kısmını döndürür.
Karşılaştığım ilginç bir şey, yakalamayan bir grubun içinde bir yakalama grubuna sahip olabilmeniz. Eşleşen web URL'leri için aşağıdaki normal ifadeye bir göz atın:
var parse_url_regex = /^(?:([A-Za-z]+):)(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
Giriş URL dizesi:
var url = "http://www.ora.com:80/goodparts?q#fragment";
Normal grubumdaki ilk grup (?:([A-Za-z]+):)
, protokol şeması ve iki nokta üst üste :
karakteri ile eşleşen bir yakalama olmayan grup yani http:
kodun altında çalışırken, ben ve kolon http
düşündüğümde döndürülen dizinin 1. dizin dize içerdiğini görüyordum her ikisi de yakalamayan bir grupta oldukları için rapor edilmeyecektir.http
:
console.debug(parse_url_regex.exec(url));
İlk grup (?:([A-Za-z]+):)
yakalamayan bir grup ise neden http
çıkış dizesinde dize döndürdüğünü düşündüm .
Yani, ([A-Za-z]+)
yakalamayan grubun içinde iç içe bir grup olduğunu fark ederseniz . Bu iç içe geçmiş grup ([A-Za-z]+)
, ?:
kendi başına bir yakalama olmayan grup içinde bir yakalama grubudur ( başlangıçta olmayan) (?:([A-Za-z]+):)
. Bu yüzden metin http
hala yakalanır, ancak :
yakalama olmayan grubun içindeki ancak yakalama grubunun dışındaki iki nokta karakteri çıktı dizisinde raporlanmaz.
Google Chrome devTools ve ardından Konsol sekmenizi açın: ve şunu yazın:
"Peace".match(/(\w)(\w)(\w)/)
Çalıştırın ve göreceksiniz:
["Pea", "P", "e", "a", index: 0, input: "Peace", groups: undefined]
JavaScript
RegExp motoru yakalama üç grup, endeksler 1,2,3 öğe bulundu. Şimdi sonucu görmek için yakalamayan işareti kullanın.
"Peace".match(/(?:\w)(\w)(\w)/)
Sonuç:
["Pea", "e", "a", index: 0, input: "Peace", groups: undefined]
Yakalamayan grubun ne olduğu açıktır.
Sanırım cevap vereceğim. Eşleşmenin başarılı olup olmadığını kontrol etmeden yakalama değişkenlerini kullanmayın.
Yakalama değişkenleri, $1
vb. Eşleşme başarılı olmadıkça geçerli olmaz ve bunlar da temizlenmez.
#!/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";
}
Yukarıdaki örnekte, bronzu yakalamayı önlemek için $1
,(?:)
kullanılır.
Desen eşleşirse, bir $1
sonraki gruplanmış desen olarak yakalanır.
Yani, çıktı aşağıdaki gibi olacaktır:
Fred wants a burger
Maçların kaydedilmesini istemiyorsanız Yararlıdır.
Son derece basit, Basit tarih örneğiyle anlayabiliriz, tarihin 1 Ocak 2019 veya 2 Mayıs 2019 olarak belirtilmiş olup olmadığını veya başka bir tarih olduğunu varsayalım ve sadece ayın dd / mm / yyyy biçimine dönüştürmek istiyoruz adı Ocak veya Şubat olan addır, bu nedenle sayısal kısmı yakalamak için (isteğe bağlı) sonek değil, yakalamayan bir grup kullanabilirsiniz.
yani düzenli ifade,
([0-9]+)(?:January|February)?
Bu kadar basit.