Düzenli İfadeler

İngilizce: Regular Expression (RegEx)

Bu konuyla ilgili Türkçe kaynak araştırdım ancak fazla bulamadım. Şu kaynaktan faydalanarak bilgilerimi pekiştirdim ve şimdi öğrendiklerimi sizlerle paylaşmak istiyorum.

Çeviride “düzenli ifadeler” anlamına gelen bu kavram daha çok bir metin içerisinde kriterlerimize uyan kelime ve olayları saptamak, düzenlemek ve değiştirmek işine yarar. Birçok programlama dilinde geçerli olan bu ifadelere ingilizce “pattern” yani kalıp denmiştir.

Örnek bir kullanımdan bahsedeceksek PHP’de yazılan aşağıdaki kodu inceleyelim:

1
2
3
   $degisken = "Ali atta bak.";
 
   echo preg_replace( '~atta~i', 'ata', $degisken );

Kodu çalıştırdığımızda “atta” kelimesini “ata” kelimesine dönüştürerek bize verdiğini göreceğiz. preg_replace kullanımı preg_replace( $kalıp, $değişecek, $değişken ) şeklindedir. Burada regex yani ifademiz ‘~atta~i’ kelimesidir.

Bu kalıp ifadeler özel bir karakterle başlatılır ve aynısıyla bitirilir, sonuna da özellik değişkenleri yazılır. Örneğin yukarıdaki örnekte “i” tek satır arama yapacağımızı belirtir. “m” birden fazla satırda işlem yaparken, “s” ise nokta işaretlerine göre arama yapmamıza yardım eder. Bunlara kalıp ayarı (Pattern Modifiers) diyoruz.

Buraya kadar genel bilgiler verdik. Şimdi kalıplarda yer verdiğimiz sembolleri tanıyalım. Öncelikle aradığımız kelimeyi ~, #, \ gibi karakterlerin arasına yazarak direkt o kelimeyi bulabiliriz. Ayrıca | işareti kullanarak birden fazla olabilecek durum yazmamız mümkün:

1
2
3
4
5
   $degisken = 'abc';
 
   $degisken = preg_replace( '~a(bc|b)~i', 'Buldum!', $degisken );
 
   echo $degisken;

Yukarıdaki kod çalıştırıldığında değişkenimizin Buldum! olarak değiştiğini göreceğiz. “a(bc|b)” de iki olasılık var. Biri abc olmasıdır, diğeri ab olmasıdır. Her iki durumda da doğru olarak sonuç dönecektir. Ancak değişkenimiz “acd” olsaydı bu kez aynı kaldığını görecektik.

Her zaman aradığımız kelime bilinen bir şey olmayabilir, belli kalıplara göre de arama yapabiliriz. Bunun için bazı özel karakterler kullanırız. Bunlardan 3 tanesini hemen tanıyalım: * (yıldız), + (artı) ve ? (soru işareti).

(*) işareti herhangi bir şey anlamında olup sıfır karaktere de olur der ve sınırsız karaktere kadar gider. (+) ise yıldızdan farklı olarak en az 1 karakter olmasını şart koşar. (?) ise tek bir harf için joker karakter niyetine kullanılır.

Bu üç işaretten önce belli bir kural belirtilir. Kural belirtmek için [] (köşeli parantez) kullanılır. Örneğin [ABC] dersek büyük harflerle A, B ve C olabilir demektir. Diğer örneklere bakalım:

[abc] : a, b, c olabilir.
[0-9] : 0 ile 9 arasındaki tüm sayılar olabilir.
[1-3] : 1 ile 3 arasındaki tüm sayılar olabilir.
[A-Z] : Büyük harfli A’dan Z’ye tüm harfler olabilir.
[a-z] : Küçük harfli A’dan Z’ye tüm harfler olabilir.
[A-Za-z0-9] : Bir harf ya da rakam olabilir.
[-_/$. ] : -, _, /, $, . ve boşluk karakterleri olabilir.

İstersek kaç adet karakter olabileceğini de limitlememiz mümkün. Bunun için { ve } kullanılır. Arasında minimum ve en yüksek karakter sayısı yazılır.

[A-Za-z0-9 ]{1,4} : Bir, iki, üç veya 4 harfli tüm rakam, harf ve boşluk karakterler olabilir.
[1-9]{3} : 100′den 999′a kadar tüm sayılar olabilir.

Şimdiye kadar öğrendiklerimizi iki örnekte görelim.

1
2
3
4
5
	$degisken = "Murat 1984 yılında Samsun'da doğdu.";
 
	preg_match_all( '/[0-9]*/i', $degisken, $esler );
 
	print_r( $esler );

Yukarıdaki kod çalıştırıldığında 1984′ün seçili olduğunu görebiliriz. Ancak kurala uyan tek kelime 1984 değil, yaklaşık 32 uyan sonuç bulunur (geriye kalanlar boştur).

Ancak yukarıdaki örneği bir de artı (+) kullanarak çalıştırsaydık:

1
2
3
4
5
	$degisken = "Murat 1984 yılında Samsun'da doğdu.";
 
	preg_match_all( '/[0-9]+/i', $degisken, $esler );
 
	print_r( $esler );

Bu kez TEK SONUÇ çıkacaktı, o da 1984 olurdu. Bu farkın sebebi * karakteri için herhangi bir karakter olmasına gerek yok ancak + simgesi kullanırsak en az 1 adet karakter gerekli.

Şimdi bir de soru işaretini kullanarak bir örnek yapalım.

1
2
3
4
5
	$degisken = "Murat 1984 yılında Samsun'da doğdu.";
 
	$degisken = preg_replace( '/Mur[A-Za-z]?t/i', 'Mert', $degisken );
 
	echo $degisken;

Bize “Mert 1984 yılında Samsun’da doğdu.” şeklinde dönüş yapacaktır. [koşul]? ile belirttiğimiz yerde “a” karakteri koşulu karşılıyor ve cümle değişiyor. Ancak “a” karakteri orada olmasaydı yani “Murt” olsaydı da Mert olarak değişecekti. Çünkü soru işareti aynı zamanda opsiyonel anlamı katar. Yani olabilir de olmayabilir de.

Başka bir örneğe bakalım:

1
2
3
4
5
	$degisken = "Programlama demek <u>matematik</u> demek değildir! <b>Mantık</b> demektir!";
 
	$degisken = preg_replace( '~<[A-Za-z][A-Za-z0-9]*>~i', '', $degisken );
 
	echo $degisken;

Çalıştırdığımızda aslında alt yazılı ve kalın olmasını sağlayan U ve B tagları varken silindiklerini göreceğiz.

Burada kural şudur: “<[A-Za-z][A-Za-z0-9]*>“, < ile başlayan, A-Za-z ile devam eden (tek karakter) ve sonrasında A-Za-z0-9 ile devam eden (yıldız süreklilik anlamı katar) ve en sonunda > ile biten bir ifade aranmaktadır. Eğer kuralımız “<[A-Za-z0-9]*>” olsaydı bu kez HTML tagları 0-9 ile de başlayabilecekti. Ancak ilk köşeli parantezde ilk harfteki zorunluluğu [A-Za-z] olarak belirtmiş olduk.

Şimdi başka bir örnekte bir sayı dizisindeki 1000 – 9999 arasındaki sayıları preg_match_all ile bulmasını sağlayalım.

1
2
3
4
5
	$degisken = '1000 752 1699 10000 25 5860 880800 9800';
 
	preg_match_all( '~\b[1-9][0-9]{3}\b~i', $degisken, $esler );
 
	print_r( $esler );

Kod çalıştırıldığında şu sonucu gösterir: “Array ( [0] => Array ( [0] => 1000 [1] => 1699 [2] => 5860 [3] => 9800 ) ) ” Görüldüğü gibi tam istediğimiz aralıktaki sayıları kendisi buldu. Bunun için kullandığımız “\b[1-9][0-9]{3}\b” kuralında \b bir kelime sınırı anlamına gelmektedir. Örneğin “Ayşe topu at” kelimesinde Ayşe’deki E harfinden sonra \b gelmektedir. Dolayısıyla verdiğimiz dizede boşlukları bölerek arama yapmasını sağlamış olduk. [1-9] ilk sayısı 1 ile 9 arasında olacağını, [0-9]{3} ise sonraki 3 karakterin sayısal bir değeri olacağını belirtir.

Dikkat edildiyse yukarıda yıldız, artı ya da soru işareti kullanmadık. Bunun sebebi kalıpta olması gerekenleri tamamen yazmış olmamız. [1-9] bir karakter, [0-9]{3} üç karakter ediyor. Toplamda 4 karakterli bir şey aradığımız kesin. Karakter sayısı bilindiğine göre +, * gibi sürekli değişkenler ile olabilir ifadesi olan ? karakterlerine gerek yoktur.

Özellikle PHP BOT yazanların sıklıkla yaptığı bir yanlışa da deyinelim. Örneğin em tagları arasındaki kelimeleri almak istiyoruz. Ancak bu durumu sağlayan iki örnek varsa aç gözlü davranıp hepsini birleştirip alır. Örneğin;

“Bu <EM>birinci</EM> test” kelimesinde <herhangibirşey> yapısındaki tüm uyan ifadeleri bulacağız. Bunun için iki farklı kullanımı aynı kodda kullanalım:

1
2
3
4
5
6
7
8
9
	$degisken = 'Bu <EM>birinci</EM> test';
 
	preg_match_all( '~<.+>~i', $degisken, $bulunan1 );
 
	preg_match_all( '~<.+?>~i', $degisken, $bulunan2 );
 
	print_r( $bulunan1 );
 
	print_r( $bulunan2 );

Çıkan sonuçlara bakacak olursak “~<.+>~i” kalıbı kullandığımız 1. komut “<EM>birinci</EM>” şeklinde tek sonuç döndürdü. Ancak soru işareti ekleyerek oluşturduğumuz 2. kalıp “~<.+?>~i” ise soru işareti olasılık anlamı kattığı için fazla açgözlü davranmadı. İki sonuç döndürdü, bunlar da istediğimiz sonuçlar: “<EM>” ve “</EM>” sonuçları.

Son olarak bir de “Ya olmamasını istiyorsak?” diyelim ve şapkalı karakterimizi tanıtalım. Bu karakter olabilecekler listesinin en önüne konulduğunda olmamasını istediklerimiz anlamı katar. Örneğin [0-9] nasıl bir sayı olmasını istediğimizi ifade ediyorsa [^0-9] sayı olmaması anlamına gelir.

1
2
3
4
5
	$degisken = 'Fetih 1453 filmine gittin mi?';
 
	$degisken = preg_replace( '#[^A-Za-z-_ ]+\b#i', '(sayı)', $degisken );
 
	echo $degisken;

Yukarıda şapkayı koyarak anlattığımız köşeli parantez içindeki ifade bir harf, – (tire), _ (alt çizgi) ve boşluk olmayan ifadeleri bul anlamına geliyor. Her kelime içinde bunu araması için +’dan sonra bir de \b koyduk. Çalıştırdığımızda 1453 olan kısmın (sayı) şeklinde olduğunu saptamış olacağız.

Aslında anlatacak çok şey var ancak giriş için temel bilgiler bunlar. Fırsat bulduğumda (?>(?:>)) tarzı özel ifadelerden faydalanmayı anlatacağım.

Hepinize iyi kodlamalar!

C# Kullanarak OBEB OKEK Hesaplamak

Bu programı tasarlarken Visual C# 2005′i kullandım. Express sürümünü internetten ücretsiz indirebilirsiniz.

Yazacağımız program temelde verilen iki sayının Ortak Katlarının En Küçüğü ile Ortak Bölenlerinin En Büyüğünü verecek. Bunun için öncelikle Visual C#’ı çalıştırıp yeni bir Windows Application projesi açıyoruz.

Formumuza;
1 adet label: üzerinde Sayı: 1 yazan,
1 adet label: üzerinde Sayı: 2 yazan,
2 adet textbox, her ikisinin de içinde 0 yazan,
2 adet button,
1 adet label: üzerinde “0″ yazan ekliyoruz. Aşağıdaki şekilde yerleştiriyoruz:

obebokek

Şimdi Form1.cs dosyasının içini açıyoruz. Öncelikle OBEB ve OKEK işlemini yapabilen fonksiyonları ekliyoruz.

OKEK için;

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
        private static int okek(int number1, int number2)
        {
            int i;
            int max = (number1 > number2) ? number1 : number2;
 
            for (i = max; i <= number1 * number2; i += max)
            {
                if (i % number1 == 0 && i % number2 == 0)
                    return i;
            }
 
            return 0;
        }

OBEB için;

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
        private static int obeb(int number1, int number2)
        {
            int i;
            int min = (number1 < number2) ? number1 : number2;
 
            for (i = min; i >= 1; --i)
                if (number1 % i == 0 && number2 % i == 0)
                    return i;
 
            return 0;
        }

Sonra 1. buttonun özelliklerinden Text kısmına OKEK yazıyoruz ve formda iki defa tıklayarak kod penceresini açıyoruz. Buraya aşağıdaki kodu yazıyoruz.

?View Code CSHARP
1
            label3.Text = okek(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text)).ToString();

2. butona aynı şekilde OBEB yazıp iki defa tıkladığımızda gelen yere aşağıdaki kodu ekliyoruz:

?View Code CSHARP
1
            label3.Text = obeb(Convert.ToInt32(textBox1.Text), Convert.ToInt32(textBox2.Text)).ToString();

İşte bitti. Çalıştırdığımızda verdiğimiz iki sayının OBEB ve OKEK değerlerini hesapladığını göreceksiniz.

Veritabanı Optimizasyonu

Bu makalede MySQL’i daha az yormak, sorguların işleniş zamanlarını kısaltmak, sitenizi hızlandırmak için birtakım veritabanı optimizasyonu tavsiyelerinde bulunacağım.

Eğer kendi kodlamalarınızla veritabanı kullanacaksanız birçok mysql sorgusu ile boğuşmak durumunda kalabilirsiniz. Kullandığınız tablolar çok fazla veri içerebilir. MySQL sunucusu bu verileri işlerken tüm veritabanındaki bilgileri kontrol etmek zorunda kalıyor olabilir. Şimdi ideal bir sorgu yapısını örnekleyelim:

1. Sadece gerekli sütunları sunucudan isteyin:

Tüm sütunları istemek demek, MySQL sunucusunun fazladan ve gereksiz veri göndermesi demektir. Örneğin bir tablodaki sadece başlık ve tarih kısmını kullanmamız gerekiyorsa bunu SELECT [sütunlar] bölümünde belirtmeliyiz.

1
2
3
4
5
# Yanlış Kullanım:
SELECT * FROM haberler LIMIT 0, 10;
 
# Doğru Kullanım:
SELECT baslik, tarih FROM haberler LIMIT 0, 10;

2. ORDER BY rand() kullanımından kaçının.

Gelen verilerin sırasını bildirmemize yarayan ORDER BY komutunun yanında eklenen rand() verilerin rastgele getirilmesini ister. Fakat bu özellikle binlerce kayıt bulunan bir tabloda sunucuya extra yük bindirir. Yaklaşık 120000 kayıtlı bir tabloda rand() kullanırsak en az 0.3 saniye sürecektir sorgumuz. Oysa ideal bir sorgu 0.001-0.005 ms harcamalıdır.

1
2
# Yanlış Kullanım:
SELECT * FROM sarki_sozleri ORDER BY rand() LIMIT 0, 10;

Eğer illa ki rastgele veri almak istiyorsanız, bu işi PHP’de de yapabileceğinizi unutmayın. Böylelikle yük MySQL’den kalkacaktır.

3. INDEX kullanın.

MySQL’de verileri hızlı bir biçimde sıralamak ve karşılığında gelen veriyi bulmak için INDEX yaratmalıyız. Özellikle en çok kullanılan sütunlar için INDEX yaratırsak sunucu daha az zamanda verileri bulacaktır:

İki yolla index ekleyebilirsiniz:

1
2
3
4
5
# 1. Yol
ALTER TABLE tablo_adi ADD INDEX index_adi (sutun1, sutun2, ...);
 
# 2. Yol
CREATE INDEX index_adi ON tablo_adi (sutun1, sutun2, ...);
1
2
3
4
5
# Örneğin aşağıdaki tarz bir sorguyu sıkça kullanıyorsak:
SELECT baslik FROM haberler WHERE dosya = "dosya_adi" AND kategori = "1";
 
# Aşağıdaki gibi bir index yaratma yoluna gidebiliriz:
CREATE INDEX l_dosya_kat ON haberler (dosya, kategori);

Böylelikle bu tarz sorgular daha az zaman harcayacaktır.

Eğer tablodaki CHAR, VARCHAR, TEXT gibi yazı içeren verilerde LIKE “%kelime%” tarzı aramalar gerçekleştiriyorsanız. FULLTEXT INDEX’i kullanabilirsiniz.

1
2
3
4
5
# Şarkı sözü tablomuzda sözlerin bulunduğu sütuna bunu ekliyoruz.
CREATE FULLTEXT INDEX l_sozler ON sarkisozleri (sozler);
 
# Ve eğer sözlerde arama yapacaksak bunu kullanmamız yeterli:
SELECT isim, soz FROM sarkisozleri WHERE MATCH(sozler) AGAINST('kelime');

Yukarıdaki örnek çalıştırılsaydı içinde “kelime” geçen tüm şarkı sözleri sorgu sonucu olarak yansıtılırdı.

Umarım sizlere faydalı olur bu yazı.

Unutmadan HTML Dersleri sayfamızdan sonra MySQL Komutları sayfamız da tamamlandı. Veritabanları ile ilgili yardım için bu siteyi kullanabilirsiniz.

Fazladan Taksimli Adresler

Bugün Google’ın sitelerimden birini adres.com/dosya// şeklinde dizinine eklediğini gördüm. Normal şartlar altında böyle olmaması gerek. Ya birileri bu şekilde bağlantı verdi ve google da bu bağlantıyı kontrol etti. Ya da bir iç kod hatası sonucu bu sayfaya ulaşabildi. Ama sorun şu ki bu adreslerin düzeltilmesi ve tek bölü işareti ile yayınlanması gerekliydi.

İnternette biraz araştırdım ve benzer sorunu yaşayabilecek arkadaşlar için HTACCESS dosyasında yapacakları küçük bir düzenlemeyle buna engel olabileceklerini öğrendim.

?View Code HTACCESS
1
2
3
RewriteEngine On
RewriteCond %{REQUEST_URI} ^(.*)//$
RewriteRule . http://www.adres.com%1/ [R=301]

Böylelikle ola ki arama motoru arkadaşçıl (SEF) adreslerle çalışan bir siteniz varsa yanlış bir indexlenmenin önüne geçmiş olacaksınız…

C# ile Hazırlanan Programı Kapatmak

C-Sharp kullanılarak hazırladığınız programı yeri geldiğinde kapatmak konusunda sıkıntı yaşayabilirsiniz. İşlemdeki bir form yüklenirken bazı kodların çalışmadığını göreceksiniz. Bu nedenle programınızı kapatmak için yerine göre aşağıdaki kodlardan birini kullamalısınız.

form.Close();
Formu kapatır. Form yüklenirken bu kod çalışmaz. Eğer program tek formdan oluşuyorsa bu aynı zamanda programı kapatmak anlamına gelecektir.

Application.Exit();
Eğer GUI kullandıysanız bu işinize yarayacaktır.

Environment.Exit(kod);
Diğer bir programı kapatma kodudur. Parantez içindeki yer bir integer değeri olup buradaki değer programın kapatılış kodudur. Bu konuda bir bilginiz yoksa 0 yazın.

Process.GetCurrentProcess().Kill();
Bu da diğer bir etkili yol. Nasıl Process.Start(“yol”) kullanılarak bir program çalıştırılıyorsa, C# kullanarak bir programı bu yolla kapatabilirsiniz. GetCurrentProcess() bir fonksiyon olup kullanılan programa işaret eder.