Bir karakter dizisi içinde istediğimiz ölçülere uygun bir alt karakter dizisi olup olmadığını veya karakter dizisinin tamamının istediğimiz ölçülere uygun olup olmadığını denetlemek için düzenli ifadeler kullanırız. Ayrıca karakter dizisi üzerinde bir çırpıda değişiklikler de yapabiliriz. Düzenli ifadeler hemen hemen tüm programlama dillerinde vardır. Javascript'te biraz daha sade olmakla birlikte işimizi görmeye yeterlidir. Düzenli ifadelerde temel amaç eşleşme kontrolüdür. Elimizde bir karakter dizisi vardır ve bunun başka bir karakter dizisi içinde olup olmadığına bakarız. Yani düzenli ifadeler tamamen karakter dizileri ile ilgilenir. İlk yapacağımız iş neyi arayacağımızı belirtmek. Yani bir düzenli ifade kalıbı oluşturmak.
var aranan = new RegExp('ne arıyorsun?');
Düzenli ifadelerin javascript karşılığı RegExp nesnesidir. Ama bu tanımlamanın daha düz bir yapısı vardır.
var aranan = /ne arıyorsun?/;
Ve genelde bu yapı kullanılır. İki tanımlama da aynı kapıya çıkar. Biz de bu ikinci yapıyı kullanacağız. Gördüğün gibi iki bölü işareti arasına ne aradığımızı yazıyoruz. Bu yapının kaynaklardaki tanımı şöyledir.
/pattern/modifiers;
pattern düzenli ifade kalıbı oluyor(iki bölü işareti arasında), yani ne arıyorsak o. modifiers ise arama yaparken, aramanın nasıl yapılacağı ile ilgili bir kaç yardımcı anahtar harf(g,i,m). Birazdan göreceğiz. Karakter dizilerinin metotlarından olan split(), search(), match() ve replace() metotları parametre olarak hem normal bir karakter dizisi, hem de düzenli ifade kalıbı alabilir. Önce en basitinden replace metodunun kullanımına bakalım.
var aranan = 'gün';
var metin = "sensiz geçen günleri ben günden saymıyorum.";
var n = metin.replace(aranan, 'dün');
console.log(n); // sensiz geçen dünleri ben günden saymıyorum.
console.log(metin); // sensiz geçen günleri ben günden saymıyorum.
Aradığımız karakter dizisini normal bir şekilde tanımladık, gün. Ve bunu dün ile değiştirmek istediğimizi belirttik. replace metodu da metin içinde bulduğu ilk gün kelimesini değiştirdi ve metnin değişiklik yapılmış halini döndürdü. Çünkü javascript'e karakter dizileri değiştirilemeyen bir veri türüdür. Bu yüzden bir karakter dizisi üzerinde değişiklik yapan tüm metotlar, değişikliği içeren yeni bir karakter dizisi döndürür. replace metodunu normalde böyle kullanıyoruz. Ama peki biz diğer gün kelimesini de değiştirmek istiyorsak? Bunu en basitinden bir döngü kurarak yapabiliriz. Ama bu şekilde metnin içinde nerede kaldığımızı bilmek için bir index değeri de tutmamız gerekir. Ayrıca değiştireceğimiz kelime metnin içinde büyük küçük harf karışık geçiyorsa bu kez döngüye if deyimleri de kaymamız gerekecek. Çoğu çok muhterem programcı amcalarımız der ki, eğer normal yoldan yapabiliyorsan düzenli ifadelere hiç bulaşma. Fakat artık çok geç, ilk düzenli ifade kalıbımızı oluşturuyoruz.
var aranan = /gün/;
var metin = "sensiz geçen günleri ben günden saymıyorum.";
var n = metin.replace(aranan, 'dün');
console.log(n); // sensiz geçen dünleri ben günden saymıyorum.
console.log(metin); // sensiz geçen günleri ben günden saymıyorum.
Sonuç öncekiyle aynı. Çünkü farklı bir sonuç elde edecek bir işaretimiz yok. Henüz.
Anahtar | Anlamı |
---|---|
g | Global arama anahtarı. Karakter dizisinin tamamını kontrol etmek için. |
i | insensitive arama anahtarı. Karakter dizisinde büyük küçük harf ayırımı yapmaz. |
m | multiline arama anahtarı. Karakter dizisindeki tüm satırları aramak için. |
Bu bilgiler ışığında, diğer gün kelimesinin geçtiği yeri de değiştirmek için global arama anahtarını kullanmamız gerek.
var aranan = /gün/g;
var metin = "sensiz geçen günleri ben günden saymıyorum.";
var n = metin.replace(aranan, 'dün');
console.log(n); // sensiz geçen dünleri ben dünden saymıyorum.
console.log(metin); // sensiz geçen günleri ben günden saymıyorum.
Düzenli ifade kalıbımız daima iki bölü işareti arasında olacak. Ve bunun hemen ardına da yardımcı arama anahtarlarını belirteceğiz. İşin içine büyük küçük harfler de girerse i anahtarını da belirtmemiz gerek.
var aranan = /gün/gi;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum.";
var n = metin.replace(aranan, 'dün');
console.log(n); // sensiz geçen dünleri ben dündEn saymıyorum.
console.log(metin); // sensiz geçen günleri ben günden saymıyorum.
Çok satırlı metinlerde tüm satırları kontrol etmek için m multiline anahtarını kullanıyoruz. Çok satırlı dediğimiz şey \n karakteri ile bölünmüş satırlardır. Fakat bu multiline anahtarı biraz garip çalışır. Tek başına kullanıldığında hiçbir etkisi yoktur.
var aranan = /gün/m;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzgün
Bu günler yalancı günler
kötüye gidiyor günbegün*/
Eğer burada m multiline anahtarını belirtmeseydik de aynı sonucu alacaktık.
var aranan = /gün/;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzgün
Bu günler yalancı günler
kötüye gidiyor günbegün*/
Üstelik burada g global anahtarını kullanırsak tüm satırlara ulaşabiliyoruz.
var aranan = /gün/g;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı günler\nkötüye gidiyor günbegün";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen dünleri ben GüNdEn saymıyorum düzdün
Bu dünler yalancı dünler
kötüye gidiyor dünbedün*/
Madem g anahtarı tüm satırlara ulaşıyor, m anahtarı ne için? Bunu birazdan daha daha iyi anlayacağız.
var aranan = /gün$/;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzgün
Bu günler yalancı bugün
kötüye gidiyor günbedün*/
Düzenli ifade kalıbımızda yeni bir karakter var. Bu karakter bir konum karakteridir ve bu şekilde javascript yorumlayıcısına, aradığımız şeyi metnin sonunda aramasını rica etmiş oluruz. Javascript de bizi kırmayıp, sonuçta görüldüğü üzere sadece metnin sonundaki gün kelimesini değiştirdi. Arada \n yeni satırlar da olmasına rağmen, tüm satırları atladı ve metnin en sonuna gitti. Eğer biz bu metnin çok satırlı(multiline) olduğunu belirtirsek sonuç değişir.
var aranan = /gün$/m;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün\nve hergün\n";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzdün
Bu günler yalancı bugün
kötüye gidiyor günbegün
ve hergün*/
Sedece ilk satırın sonuna kadar gitti bu kez. Zîra biz ona global bir arama yapmasını söylemedik. Söyleyelim o zaman.
var aranan = /gün$/mg;
var metin = "sensiz geçen günleri ben GüNdEn saymıyorum düzgün\nBu günler yalancı bugün\nkötüye gidiyor günbegün\nve hergün\n";
var n = metin.replace(aranan, 'dün');
console.log(n);
/* sensiz geçen günleri ben GüNdEn saymıyorum düzdün
Bu günler yalancı budün
kötüye gidiyor günbedün
ve herdün*/
Bu kez tüm metni aradı ve her satırın sadece sonuna baktı. Multiline bu şekilde bir fayda sağlıyor işte. İleride daha da iyi anlayacağız tabi.
Karakter | İşlevi |
---|---|
^ | Düzenli ifade kalıbının, karakter dizisinin sadece başında aranacağını belirtir. Eğer m multiline anahtarı kullanılırsa her satırın sadece başında arama yapılır. |
$ | Düzenli ifade kalıbını karakter dizisinin sonunda arar. m multiline anahtarı belirtilirse her satırın sonunda arama yapılır |
\b | Aramanın kelime sınırında olacağını belirtir. Kelime sınırının başlangıcı bir karakterle başlar ve karakter olmayan(boşluk gibi) bir karakterle biter |
\B | Aramanın kelime sınırında olmayan yerlerde yapılacağını belirtir. |
Şu kelime sınırı olayına hemen bakalım.
var aranan = /\b/g;
var metin = "sen seni bil sen seni";
var n = metin.replace(aranan, '|');
console.log(n);
/*|sen| |seni| |bil| |sen| |seni| */
Gayet açık değil mi? Kelime sınırı dediğimiz şey, kelimelerin başlangıç ve bitiş yerleri. Bu durumda kelime sınırı olmayan yerler de ortaya çıkmış oluyor sanırım.
var aranan = /\B/g;
var metin = "sen seni bil sen seni";
var n = metin.replace(aranan, '|');
console.log(n);
/* s|e|n s|e|n|i b|i|l s|e|n s|e|n|i */
//console.log(String.fromString(metin));
Mantıklı bir sonuç değil mi? Bu kez kelimelerin başlangıç ve bitiş yerleri değil, bu başlangıç ve bitiş yerlerinin arasında kalan, kelime başlangıcı ve bitişi olmayan tüm yerler işleme tâbi tutuldu. Fakat bu kelime sınırı kosunda ingiliz alfabesi dışındaki dillerde büyük bir sorun var.
var aranan = /\b/g;
var metin = "sensiz geçen günleri ben günden saymıyorum";
var n = metin.replace(aranan, '|');
console.log(n);
// |sensiz| |ge|ç|en| |g|ü|nleri| |ben| |g|ü|nden| |saym|ı|yorum|
Bu sorunun henüz müstakil bir çözümü bulunmamakta ama bu konuyu yiyip bitirdikten sonra ancak bu soruna çözüm üretebileceğimizi düşünmekteyim. O yüzden bu aşamada bu sorunla uğraşmak bize vakit kaybettirir. Şimdilik kelime sınırı konusunda bu şekil kullanımlardan kaçınmamız gerek. Veya bir çözüm önerin varsa lütfen face'den dürt beni.
Diğer bir konum karakteri olan ^ karakteri şu şekilde.
var aranan = /^gün/g;
var metin = "günlerim sensiz geçiyor\ngünlerim ağlak";
var n = metin.replace(aranan, 'dün');
console.log(n);
/*dünlerim sensiz geçiyor
günlerim ağlak*/
Karakter dizisinin sadace en başına baktı, diğer satıra bakmadı, çünkü çok satırlı olduğunu belirtmedik, o yüzden karakter dizisini bir bütün olarak ele aldı. Diğer satıra da bakması için tabiki m multiline anahtarını balirtmemiz gerek.
var aranan = /^gün/gm;
var metin = "günlerim sensiz geçiyor\ngünlerim ağlak";
var n = metin.replace(aranan, 'dün');
console.log(n);
/*dünlerim sensiz geçiyor
dünlerim ağlak*/
Böyle sürekli düzenleme üzerinden gidiyoruz ama düzenli ifadeler daha çok denetleme amacıyla kullanılır. Yani bir eşleşme aranır. Bunun için RegExp nesnesinin kendine has metotları olan exec() ve test() metotları kullanılır. test metodu, karakter dizisini test eder ve aradığımız eşleşme varsa true döndürür, yoksa false.
var aranan = /gün/;
var metin = "günlerim sensiz geçiyor";
var n = aranan.test(metin);
console.log(n); // true
var aranan = /ay/;
var metin = "günlerim sensiz geçiyor";
var n = aranan.test(metin);
console.log(n); // false
Bu metot RegExp nesnesinin metodu olduğu için oluşturduğumuz RegExp nesnesi üzerinden kullanıyoruz metodu. exec metodu ise şu şekilde.
var aranan = /gün/;
var metin = "günlerim sensiz gün gibi geçiyor";
var n = aranan.exec(metin);
console.log(n); // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor"]
Bu metot bir dizi döndürüyor. Sonuca göre aradığımız şey karakter dizisinde var ve 0. index'ten başlıyor. Bu metotları kullandığımızda, RegExp nesnesine ait olan lastIndex özelliği de sonuca göre güncellenir. lastIndex özelliği, yukarıdaki arama sonucunda 0 değerini taşır. Fakat eğer global bir arama yaparsak bu lastIndex özelliği eşleşmeden hemen sonraki index'i gösterir.
var aranan = /gün/g;
var metin = "günlerim sensiz gün gibi geçiyor";
var n = aranan.exec(metin);
console.log(aranan.lastIndex); // 3
console.log(n); // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor"]
Eğer eşleşme yoksa exec metodu null döndürür ve lastIndex ise 0 değerini alır. Eğer global arama yapmıyorsak, eşleşme olsa da olmasa da lastIndex daima 0 olur.
var aranan = /ay/;
var metin = "günlerim sensiz gibi geçiyor";
var n = aranan.exec(metin);
console.log(aranan.lastIndex); // 0
console.log(n); // null
Tekrar string metotlarına dönecek olursak match() metodu, eğer global arama yapmıyorsak exec() metodu gibi bir sonuç döndürür.
var aranan = /gün/;
var metin = "günlerim sensiz gün gibi geçiyor gün";
var n = metin.match(aranan);
console.log(n); // ["gün", index: 0, input: "günlerim sensiz gün gibi geçiyor gün"]
Global arama yapıyorsak ise şöyle:
var aranan = /gün/g;
var metin = "günlerim sensiz geçiyor";
var n = metin.match(aranan);
console.log(n); // ["gün"]
var aranan = /gün/g;
var metin = "günlerim sensiz gün gibi geçiyor gün";
var n = metin.match(aranan);
console.log(aranan.lastIndex); // 0
console.log(n); // ["gün", "gün", "gün"]
Eğer eşleşme olmazsa null döndürür.
var aranan = /ay/g;
var metin = "günlerim sensiz gün gibi geçiyor gün";
var n = metin.match(aranan);
console.log(n); // null
String metotlarının bir diğer ise search metodu idi. Bu metot ilk bulduğu eşleşmenin karakter dizisi içindeki index'ni verir, yoksa -1 döndürür.
var aranan = /gün/;
var metin = "günlerim sensiz gün gibi geçiyor gün";
var n = metin.search(aranan);
console.log(n); // 0
Global arama yapsak da sonuç değişmez, bulduğu ilk eşleşmenin index'ini verir.
var aranan = /gün/g;
var metin = "Günlerim sensiz gün gibi geçiyor gün";
var n = metin.search(aranan);
console.log(n); // 16
Evet yavaş yavaş giriyoruz düzenli ifadelere. Evet yanlış okumadın, henüz düzenli ifadelere girmedik, sadece kapıdan el salladık. Buraya kadar cephanemizi, mataramızdaki suyu yokladık ve eksiklerimizi bir parça giderdik. Artık düzenli ifadelere girmemizin vakti geldi çattı.
Neden düzenli ifadeler?
Bu soruyu cevaplandırmayacağız bu başlık altında. E niye böyle bir başlık attın o zaman? Çeşitli konulardan bahseden intenet eşrâfı kendine böyle bir âdet edinmiş, bahsettiği konu ile ilgili mutlaka neden falan, neden filan diye başlıklar atıp altında da ileri geri yazıp dururlar. Ben de hep özenmişimdir bu neden filan olayına. O yüzden böyle bir başlık attım. Kendimi çok iyi hissediyorum. Pişman değilim. Bu düzenli ifadelerin amacı konusunda yeteri kadar altı çizili laflar ettim mi bilmiyorum, çünkü çoğu kişi, çoğu yaygın kanı düzenli ifadelerden amacının üstünde şeyler bekler. Veya konuya öyle bir önyargı ile girer. Bu önyargı, bu yanlış görüş, bu çarpık düşünce, düzenli ifadelerin arama yapma konusunda üstün olduğu ve bu yönde kullanıldığı. Evet arama da yapıyoruz düzenli ifadelerle ama ana amaç bu değil ve şöyle biraz düşünürsen arama konusunda düzenli ifadelerin zayıf olduğunu kolayca anlarsın. Yukarıdan beri yaptığımız örneklerde bu belli zaten. Mesela arama yaptık diyelim search metoduyla, veya eşleşme aradık match metoduyla veya diğerleriyle. Hiçbiri de bulduğu tüm eşleşmeleri metnin tamamında nerde bulduğunu bildirmiyor. Sadece ilk bulduğunu bildiriyor. Mesela bir metnin içinde geçen tüm filan kelimelerinin yerlerini tek tek tespit etmek istersek extra bir döngü kurmamız gerekir. Eğer böyle bir arama için döngü kuracaksam şahsen düzenli ifadeleri kullanmam. String metotlarını normal şekilde kullanmak bu durumda daha hızlı ve daha verimlidir. String metotlarının normal kullanımı düzenli ifadelerden her zaman daha hızlı çalışır. Ve programlama câmiyasında hız ve verimlilik dini bir vecibe gibidir. Harikalar da yaratsan yazdığın kodlarla, hızlı ve verimli değilse at çöpe. Eğer amacımız saf arama yapmaksa düzenli ifadelerle işimiz yok. Gerçi bu biraz da javascript'ten kaynaklanıyor, çünkü diğer programlama dillerinde düzenli ifadeler çok daha geniş kapsamlı ve çok daha çetrefilli. Bunun en büyük sebebi javascript'i bugüne kadar kisenin adam yerine koymaması. Ama bugün javascript'in girmediği delik kalmadı. Herşeye rağmen düzenli ifadelerin geneline baktığında bir kontrol sözkonusudur. Ana amaç kontroldür yani. Düzenli ifadelerle kullanılan en etkin metot replace metodudur değişiklik üzerine. Diğerleri saf kontrol. Bu konunun altını çizdiğimize göre yolumuza devam edebiliriz.
Karakter Sınıfı | İşlevi |
---|---|
\d | 0-9 arası herhangi bir sayısal karakterin yerine geçer |
\D | Sayısal olmayan herhangi bir karakterin yerine geçer |
\w | A-Z, a-z, 0-9, _, arasında herhangi birinin yerine geçer. Bunların hepsine birden kelime karakterleri deniliyor. |
\W | Kelime karakteri olmayan herhangi bir karakterin yerine geçer. |
\s | Boşuk, tab gibi whitespace denen karakterlerinin yerine geçer. |
\S | whitespace olmayan herhangi bir karakterin yerine geçer. |
. | Yeni satır karakteri \n hariç herhangi bir karakterin yerine geçer. |
[...] | Parantezin içine yazılacak karakterlerden herhangi birinin yerine geçer. Mesela [abc], bu karakterlerin herhangi birinin yerini tutar. |
[^...] | Parantezin içine yazılacak karakterlerin dışında herhangi bir karakterin yerine geçer. Mesela [^abc], bu karakterler hariç anlamına gelir. |
Hepsini tek tek inceleyeceğiz ama istersen nicelik belirten özel karakterlerin de tablosunu görelim ve iki tabloyu içiçe birlikte inceleyelim
Özel karakter | Anlamı |
---|---|
{n} | Kendinden önce gelen karakterin n kez tekrar edeceğini belirtir. |
{n,} | Kendinden önce gelen karakterin n kez yada daha fazla tekrar edeceğini belirtir. |
{n,m} | Kendinden önce gelen karakterin en az n en çok m kez tekrar edeceğini belirtir. |
? | Kendinde önce gelen karakterin 0 yada 1 kez tekrar edeceğini belirtir. |
+ | Kendinde önce gelen karakterin 1 yada daha fazla kez tekrar edeceğini belirtir. |
* | Kendinde önce gelen karakterin 0 yada daha fazla kez tekrar edeceğini belirtir. |
\d
Anlamının gayet açık olduğunu düşünüyorum.
var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];
Elimizde böyle bir dizi var ve biz sayıyla başlayıp sayıyla biten elemanları ayıklamak istiyoruz. Hem yukarıdaki tablolara bakalım hem de bağıra bağıra düzenli ifademizi kurmaya başlayalım.
/\d \d/
Bu kadarını hepimiz anladık, zaten bu başlık altındayız. Peki aradaki boşluğa ne gelecek? Bana bakma, yukarıdaki tabloya bak. Tablodaki . (nokta) karakteri senin de dikkatini çekmiş olmalı. Evet aradığımız şey o. İster sayı olsun ister harf olsun ister başka birşey olsun farketmez, yeterki bir şey olsun anlamına geliyor bu nokta. Fakat araya kaç tane . (nokta) koyacağımız kesin değil, çünkü elemanların karakter sayıları birbirinden farklı. Demekki neymiş, bize bir yardımcı oyuncu daha lazımmış. Bu kriterimiz ise en az bir karakter olması, sayı veya harf farketmez, en az bir tane olduktan sonra gerisi önemli değil. Bu kritere uyan + karakterini görüyoruz tabloda değil mi? Bunu nokta karakterinin ardına koyursak bu iş olur gibi. Yani
var aranan = /\d.+\d/;
böyle oldu anlattıklarımıza göre. Bir sayı ile başlıyor \d, sonra en az bir ve daha fazla tekrar eden herhangi bir karakter .+ ve sonra yine sayı ile bitiyor \d. Şimdi test edelim.
var aranan = /\d.+\d/;
var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
if(aranan.test(metin[x])){
console.log(metin[x]);
}
}
/*12a45fyt
343ed45
36h6u8iu
edfg4098
34gh89ol
erf456hy
98hkyit5*/
Sonuç tam bir fiyasko. Peki neden böyle oldu? İstediğimiz ölçü sayı ile başlayıp sayı ile bitmesiydi ama elemanların hepsi geldi. Halbuki dizide sayıyla başlayıp sayıyla biten sadece iki eleman var. Biz /\d.+\d/ bu tanımlamayla, sayı ile başlayıp sayı ile bitmesini belirtecek bir işaret koymadık aslında. Yani bu ölçümüz elemanın herhangi bir yerinde geçse de uygun olacak şekilde. Peki başlangıç ve bitişi nasıl belirtiyorduk? \b karakteriyle değil mi? Yani kelime sınırı. Hemen deneyelim.
var aranan = /\b\d.+\d\b/;
var metin = ['12a45fyt', '343ed45', '36h6u8iu', 'edfg4098', '34gh89ol', 'erf456hy', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
if(aranan.test(metin[x])){
console.log(metin[x]);
}
}
/*343ed45
98hkyit5*/
Evet bu kez amacımıza ulaştık. Ama yukarıda bir yerlerde bu kelime sınırı karakterinin türkçe harflere karşı alerjisini görmüştük. Listemizdeki elemanların içinde türkçe harfler olsaydı yine istemediğimiz sonuçlarla karşılaşırdık.
var aranan = /\b\d.+\d\b/;
var metin = ['12a4ğ5fyt', '343edı45', '36h6u8iu', 'edfgç4098', '34gh89ol', 'erf456hy', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
if(aranan.test(metin[x])){
console.log(metin[x]);
}
}
/*12a4ğ5fyt
343edı45
edfgç4098
98hkyit5*/
Evet istenmeyen elemanlar da geldi. Peki bu durumda ne yapacağız? Elimiz kolumuz bağlı mı oturacağız? Tabiki hayır. Yukarıdan beri öğrendiğimiz bir iki kungfu tekniğiyle bu sorunu halledeceğiz. Ölçümüz neydi? Sayı ile başlayıp sayı ile bitsin. Yani elemanın hem başında hem sonunda sayı olsun. Peki biz bunu başka bir düzenli ifade ile ifade edemez miyiz?
var aranan = /^\d.+\d$/;
var metin = ['12a4ğ5fyt', '343edı45', '36h6u8iu', 'edfgç4098', '34gh89ol', 'erf456hy', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
if(aranan.test(metin[x])){
console.log(metin[x]);
}
}
/*343edı45
98hkyit5*/
Düzenli ifademiz /^\d.+\d$/ böyle oldu ve sorunumuz çözüldü. ^ karakteri aradığımız ölçünün elemanın başında olduğunu, $ karakteri ise sonunda olduğunu belirtiyor. Fakat bu çözüm sadece bu sorunu ve buna benzer sorunları çözer. Yani kelime sınırı karakteri ile ilgili hâlâ ciddi sorunlarımız olduğunu aklından çıkarma. Ayrıca bu neticeden şunu çıkarıyoruz, bir düzenli ifade kalıbı, birden çok şekilde ifade edilebilir. Yani o anki duruma uygun aklımıza gelen en basit ifadeyi kullanırız.
var metin = [123', '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];
Listemiz böyle olsaydı ve biz sadece sayı içeren elemanları ayıklamak isteseydik.
var aranan = /\d+/;
var metin = [123, '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
if(aranan.test(metin[x])){
console.log(metin[x]);
}
}
/*123
343edı45
456
789
98hkyit5*/
Bu ilk denememiz de yine fiyasko ile sonuçlandı. Çünkü yine aynı hatayı yaptık. /\d+/ bu tanımlama ile en az bir ve daha fazla sayı karakteri elemanın içinde de olsa ver demiş olduk. Aslında bu eşleşmenin tam olarak nasıl olduğunu match metodu ile görebiliriz. match metodu eşleşme olursa bize bir dizi döndürüyordu değil mi? Ve bu dizinin ilk ve tek elemanı yapılan eşleşmeyi tutuyordu. Ve ayrıca dizinin bir de input diye bir özelliği vardı, bu da eşleşmenin yapıldığı kaynak string'i tutuyordu. Biz bu iki bilgiye bakarak hangi elemanların nasıl eşleştiğini görebiliriz. Ama match metodu karakter dizilerinin metodu olduğu olduğu için listedeki gerçek sayıları da string'e çevirmemiz gerek değil mi?
var aranan = /\d+/;
var metin = [123, '343edı45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
var y;
if( y = metin[x].toString().match(aranan)){
console.log(y[0], y.input);
}
}
/*123 123
343 343edı45
456 456
789 789
98 98hkyit5*/
Evet, hangi eşleşmenin hangi elemanla olduğu gayet açık ve hangi mantıkla bize sayı olmayan elemanları verdiğini gayet net bir şekilde görüyoruz. Aradığımız ölçü elemanların içinde de geçiyor. Biz kelime sınırını belirtmediğimiz için sonuç böyle oldu.
var aranan = /\b\d+\b/;
var metin = [123, '343ed45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
var y;
if( y = metin[x].toString().match(aranan)){
console.log(y[0], y.input);
}
}
/*123 123
456 456
789 789*/
Bu kez eşleşmeleri net bir şekilde aldık. Kullandığımız karakterlere dikkat et. \b kelime sınırı ile başlıyor, \d+ en az bir veya daha fazla sayısal karakterle devam ediyor ve \b kelime sınırı ile bitiyor. Aradığımız elemanlar listede aslında üç basamaklı üç sayı. Yani bunu /\d\d\d/ şeklinde de belirtebilirdik. Gerçi baştaki eleman da gelirdi ama, demek istediğim \d+ ifadesi ile biz bir veya daha fazla basamaklı sayıyı temsil ediyoruz. Yani burada aslında bir nevi üstü kapalı bir konuşma var. Ve üstü kapalı bu konuşma bir genelleme oluşturuyor ve bir çok elemanı aynı anda kastetmiş oluyoruz. Düzenli ifadelerin yaptığı şey tamamen budur işte.
var aranan = /\b\D+\b/;
var metin = [123, '343ed45', 456, 'hebele', '789', 'yağmur', '98hkyit5'];
for(var x = 0; x < metin.length; x++){
var y;
if( y = metin[x].toString().match(aranan)){
console.log(y[0], y.input);
}
}
/*hebele hebele
yağmur yağmur*/
Bu kez elemanın sayı içermemesini istedik \D karakteri ile.
\w
Bu karakter sınıfı ile, elle tutulup gözle görülen, bir ağırlığı ve hacmi olup uzayda yer kaplayan tüm karakterleri temsil edebiliriz. Buna bazı noktalama karakterleri dahil değil ama _ alt tire dahil. Gavurların whitespace dediği, gözle görülüp elle tutulmayan, uzayda yer kaplamayan, boşluk tarzı karakterler bu grubun dışında kalıyor. Dışarda kalan bu grubu ise \W karakter sınıfı üstleniyor.
var metin = 'gelecek ay 24 mart çarşamba. hava güneşli';
Bu yazıdan sadece 24 mart ifadesini ayıklamak istiyoruz. İlk göze çarpan ölçümüz yanyana iki sayı karakteri. Bunu \d{2} bu şekilde karşılayabiliriz değil mi? Sayımız tamı tamına 2 kez tekrar edecek. Sonra arada bir boşluk var. Bunu da \s karakteri karşılar değil mi? Son olarak da \w karakteri ile kalanları temsil edebiliriz. Ve kelimenin sonunda da bitmeli. Yani \b.
var aranan = /\d{2}\s\w+\b/;
var metin = 'gelecek ay 24 mart çarşamba. hava güneşli';
var n = metin.match(aranan);
console.log(n[0]); //24 mart
Bu kez bir hamlede almayı başardık. Ama bunu kutlamadan önce sana kötü bir haber vermek istiyorum. \w karakter sınıfının da türkçe karakterlere alerjisi var.
var aranan = /\d{2}\s\w+\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';
var n = metin.match(aranan);
console.log(n); // null
null sonucunu yüzümüze bir tokat gibi yedik. Halbuki biz adımız gibi biliyoruzki orada alfanumerik bir karakter var. Anladık bizi sevmiyorsunuz da, alfabemizle ne sorununuz var be kardeşim. Açıkçası bu gibi sorunlar çok canımı sıkıyor. Bütün keyfim kaçıyor bir anda. Bu sorunu da ifademizi değiştirerek çözeceğiz.
var aranan = /\d{2}\s.+\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';
var n = metin.match(aranan);
console.log(n[0]); // 24 şubat çarşamba. hava güneşli
Bu şekilde istemediğimiz yerler de geldi. O yüzden daha net bir tanım yapmamız gerekiyor. Ama yukarıda bahsettiğim gibi, düzenli ifadelerin gücü muammadan kaynaklanıyor. Yani sen düzenli ifadeni netleştirdikçe kapsam alanın daralıyor. Bu noktada karakter dizisini de iyi analiz etmek gerek. Bilmediğimiz bir dizide net tanımlarla işlem yapamayız. Ama burada ne olduğu bariz belli.
var aranan = /\d{2}\s.{5}\b/;
var metin = 'bugün 24 şubat çarşamba. hava güneşli';
var n = metin.match(aranan);
console.log(n[0]); // 24 şubat
Burada karakter dizisini tanımak bize avantaj sağladı ama bunun her zaman böyle olmayacağı ortada bir gerçek. Javascript bu konularda çok eksik. Ama biz onu eksiklerine rağmen seviyoruz değil mi? İşte gerçek sevgi budur.
var metin = 'benim.,= numaram ^+haftaya...,,. hava,,...***?? sisli';
Burada noktalama işaretleri haricindeki yazıyı ayıklamak istiyoruz. Hariç tutmak için yukarıdaki tabloda [^ ] özel karakterini görüyoruz. Hemen deneyip bakalım.
var aranan = /[^\w]/g;
var metin = 'benim.,=& numaram ^+haftaya...,,. hava,,...***?? sisli';
var n = metin.replace(aranan, '');
console.log(n); // benimnumaramhaftayahavasisli
Diğer karakterleri eledik ama sonuç çok da istediğimiz gibi değil. Bunun daha anlamlı olması için aralarında en azından bir baoşluk olsa fena olmazdı değil mi?
var aranan = /[^\w\s]/g;
var metin = 'benim.,=& numaram ^+haftaya...,,. hava,,...***?? sisli';
var n = metin.replace(aranan, '');
console.log(n); // benim numaram haftaya hava sisli
Bu şekilde boşlukları da hariç tutmuş olduk. Ayrıca global bir arama yaptığımıza da dikkat etmişsindir sanırım. Yoksa sadece ilk eşleşmede olay bitecekti değil mi? Kısacası \w karakteri tüm alfanumerik karakterlerin yerine geçiyor. Sanırım olayın nasıl yürüdüğünü anladık. Biraz daha örnek yapıp konuyu kapatalım.
var aranan = /^at|at$/g;
var metin = ['at', 'kat', 'yat', 'sat', 'yatırım', 'satış', 'batak', 'yatak', 'rahat', 'kabahat', 'atak'];
for(let x = 0; x < metin.length; x++){
if(metin[x].match(aranan)){
console.log(metin[x]);
}
}
/*at
kat
yat
sat
rahat
kabahat
atak*/
Burada yeni bir özel karakter kullandık. | karakteri hem at ile başlayan hem at ile biten elemanları almamızı sağladı. Bir nevi veya anlamına geliyor. Eğer bunu kullanmazsak:
var aranan = /^at$/;
var metin = ['at', 'kat', 'yat', 'sat', 'yatırım', 'satış', 'batak', 'yatak', 'rahat', 'kabahat', 'atak'];
for(let x = 0; x < metin.length; x++){
if(metin[x].match(aranan)){
console.log(metin[x]);
}
}
/* at */
Hem at ile başlayıp hem at ile biten elemanı verdi. Ama bunda bir nevi ve anlamı var. O yüzden her iki özelliği aynı anda taşıyan elemanı verdi.
Bu arada önemli bir şey unutmuşuz ya. split() fonksiyonunu.
split()
Bu metot da karakter dizilerinin metotlarından biri ve buna da argüman olarak düzenli ifade kalıbı verebiliriz. Daha önce bahsetmemiz gerekirdi ama unutmuşum. Sen de hiç hatırlatmıyorsun ama. Ne kadar ayıp. Bu fonksiyon gayet kullanışlı ve çok işimize yarayacak bir fonksiyon. Bir karakter dizisini belirli bir yerden bölmek için kullanılır. Karakter dizisini böler ve bu bölünen parçaları bir dizi halinde verir.
var aranan = /\/\//;
var metin = 'http://google.com';
var n = metin.split(aranan);
console.log(n);
/* ["http:", "google.com"] */
Düzenli ifade kalıbımız biraz garip görünüyor haklısın. Bunun sebebi özel anlama gelen karakterleri kullanırken, bu özel anlamını değil de normal bir karakter anlamını, yani kendini temsil etsin diye önüne \ ters bölü işareti koymamız. Biz burada yanyana iki bölü // işaretini yakalamak istediğimiz için her birinin önüne ters bölü \ koyduk. Bu, tüm özel anlam ifade eden karakterler için geçerli. Mesela $ karakteri düzenli ifade içinde kullanıldığında karakter dizisinin sonuna bakılacağını belirtiyor ama peki biz gerçekten de bu dolar işaretini arıyorsak?
var aranan = /\d\d$/;
var metin = 'http://google.com?20$';
var n = metin.match(aranan);
console.log(n);
/* null */
Gördüğün gibi javascript bizim yazının sonuna bakmak istediğimizi düşünüyor. Biraz yardıma ihtiyacı var.
var aranan = /\d\d\$/;
var metin = 'http://google.com?20$';
var n = metin.match(aranan);
console.log(n);
/* ["20$", index: 18, input: "http://google.com?20$"] */
Bir html elemetini html kodlarına çevirmeyi deneyelim. Çevireceğimiz element yukardaki son başlık olsun. Yani şu: <h3>split()</h3>. Yani sayfada ben yazarken böyle görünüyor, ama senin bu şekilde görmen için bunu html kodlarına çeviriyorum. Bu işi bir de biz deneyelim.
var elemanlar = ['<', '>', '\/', '\\(', '\\)'];
Bunlar çevireceğimiz elemanlar. Parantezler için iki ters bölü kullandık özel anlamlarından dolayı. Çünkü parantezler de düzenli ifade içinde özel anlam taşıyor. Birazdan bununla ilgili örnek yapalım. Peki bunları neye çevireceğiz?
var karşılıklar = ['&lt;', '&gt;', '&sol;', '&lpar;', '&rpar;'];
İşte bunlar da html kod karşılıkları. Hatta bunu bir tablo olarak görsek sanki daha iyi olacak gibi.
Karakter | html kodu |
---|---|
< | < |
> | &gt; |
/ | &sol; |
( | &lpar; |
) | &rpar; |
Şimdi çevirebiliriz.
var elemanlar = ['<', '>', '\/', '\\(', '\\)'];
var karşılıklar = ['&lt;', '&gt;', '&sol;', '&lpar;', '&rpar;'];
var metin = '<h3>split()</h3>';
for(let x = 0; x < elemanlar.length; x++){
let aranan = new RegExp(elemanlar[x], 'g');
metin = metin.replace(aranan, karşılıklar[x]);
}
console.log(metin); // &lt;h3&gt;split&lpar;&rpar;&lt;&sol;h3&gt;
Döngünün her turunda elemanlar dizisinin bir elemanıyla düzenli ifade kuruyoruz ve yine aynı sıradaki karşılığını takas ediyoruz. İki diziyi bu yüzden birbirine paralel yaptık. Ve her turda metin değişkeni dönen yeni değerle yenileniyor ve sonunda tamamen kodlanmış hale geliyor.
gruplama
Düzenli ifade içinde bazı karakterleri () parantez içine alarak gruplayabiliriz.
var metin = 'bu araba harika. ama şurada çok daha güzel arabalar var.';
var aranan = /araba(lar)?/g;
var n = metin.replace(aranan, 'kamyon');
console.log(n); // bu kamyon harika. ama şurada çok daha güzel kamyon var.
Parantez içinde aldığımız (lar) ifadesine toplu şekilde ? özel karakterini uyguladık. Yani lar kelimesi hiç geçmese de olur, bir kere geçse de olur demiş olduk.
var metin = 'kamyonculuk kamyon sürmekle olur. kamyonsuz kamyoncu mu olurmuş.';
var aranan = /kamyon(cu|suz)/g;
var n = metin.match(aranan);
console.log(n); // ["kamyoncu", "kamyonsuz", "kamyoncu"]
Parantez olayının bazı ilginç özellikleri de var.
var metin = 'benim adım ebruli';
var aranan = /(benim)\s(adım)\s(ebruli)/;
var n = metin.replace(aranan, '$3 $1 $2');
console.log(n); // ebruli benim adım
Parantez içine aldığımız elemanlar soldan sağa doğru birinci parantez $1, ikici $2 üçüncü $3 falan diye gidiyor.
x(?=y)
x ancak arkasından y geliyorsa eşleşir.
var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?=cu)/g;
var n = metin.match(aranan);
console.log(n); // ["kamyon", "kamyon"]
Burada eşleşmeyen en baştaki kamyonu kelimesi. Çünkü arkasından cu ifadesi gelmiyor. Bunu şöyle daha iyi görebiliriz.
var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?=cu)/;
metin = metin.split(' ');
for(let x = 0; x < metin.length; x++){
var eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* kamyon kamyoncu
kamyon kamyonculuk */
x(?!y)
x ancak arkasından y gelmiyorsa eşleşir.
var metin = 'kamyonu kamyoncu sürer. kamyonculuk böyle olur';
var aranan = /kamyon(?!cu)/;
metin = metin.split(' ');
for(let x = 0; x < metin.length; x++){
var eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* kamyon kamyonu */
Yukarıda menzil dışında kalan şimdi içerde. Çünkü kamyonu kelimesinde, kamyon kısmından sonra cu ifadesi yok.
var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /(es[mr][ae])(?=.)/;
for(let x = 0; x < metin.length; x++){
let eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* esme esmehan
esme esmelan
esma esmanur
esme esmer
esra esragül*/
Burada ise esma esra isimlerini yalın olarak verme demiş olduk, devamında mutlaka bir ek olmalı. Bunun tersini yaparsak:
var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /(es[mr][ae])(?!.)/;
for(let x = 0; x < metin.length; x++){
let eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* esra esra
esma esma*/
Böyle yalın şekilde almış oluruz.
var metin = ['esra', 'esma', 'esmehan', 'ismihan', 'esmelan', 'esmanur', 'esmer', 'esragül'];
var aranan = /es[mr]a/;
for(let x = 0; x < metin.length; x++){
let eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* esra esra
esma esma
esma esmanur
esra esragül */
var metin = ['257654', '4537', '87789', '123', '32212', '0987', '32221', '3890'];
var aranan = /[1-3]{3}/;
for(let x = 0; x < metin.length; x++){
let eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* 123 123
322 32212
322 32221 */
Burada ise 1 ile 3 arasında yanyana 3 sayı içeren elemanları arıyoruz.
var metin = ['257654', '4537', '87789', '123', '32212', '0987', '32221', '3890'];
var aranan = /[3-7]{3}/;
for(let x = 0; x < metin.length; x++){
let eşleşme = metin[x].match(aranan);
if(eşleşme){
console.log(eşleşme[0], eşleşme.input);
}
}
/* 576 257654
453 4537 */
3 ile 7 arasında yanyana 3 sayı arıyoruz.
console.log(
'beni sana hapsettin'
.replace( /[aeouoöüıi]/g, '' )
); // bn sn hpsttn
Bu kez sesli harfleri eledik.
Emeğinize sağlık.
YanıtlaSil