Hakkında Künye

Düzenli İfadeler-3

Düzenli ifadeler serimizin son yazısını, bir aylık aradan sonra sizlere sunuyoruz. Geçtiğimiz aylardaki iki yazımızda regular expression adı verilen ifadeleri ve kullanım alanlarını tanıtıp, çeşitli programlama dillerinde nasıl kullanabileceğimizi örneklerle gördük. Bu ay, önceki yazılarımızda belirtmediğimiz birkaç önemli noktaya değinecek ve sık kullanılan çeşitli kalıpları göreceğiz.

Düzenli İfade Türleri

Temel Düzenli İfadeler

POSIX standartında yer alan basic regular expressions (BRE), en genel kabul görmüş düzenli ifade standartlarından biridir. İlk yazımızda yer alan sözdizimi, temel düzenli ifade standardına uygundur. Bu standart, DFA (Deterministic Finite Automata)'lar tarafından kabul edilen dille, yani düzenli ifadelerin teorik temelleriyle örtüşür.

Genişletilmiş Düzenli İfadeler

Temel düzenli ifadeler, kullanım kolaylığı sağlanması için farklı özellikler eklenerek geliştirilmiş ve genişletilmiş düzenli ifadeler, yani extended regular expression (ERE) standardı oluşturulmuştur. ERE de POSIX standardında yer almaktadır. Genişletilmiş düzenli ifadelerde kullanılabilen ek ifade yöntemleri şunlardır:

Gösterim İfade Eşleşme Açıklama
? (e-)?bergi bergi
e-bergi
Soru işareti, kendinden önce gelen parçanın metin içinde 0 veya 1 kere yer alacağını belirtir.
+ izmir+ izmir
izmirrr
Artı sembolü, önceki düzenli ifade parçasının en az bir kere geçmesi gerektiğini belirtir. Bir üst limit yoktur.
| (b|d)ergi bergi
dergi
"veya" sembolü, iki tarafındaki regular expression parçalarından birinin bulunması gerektiğini ifade eder.

Perl Düzenli İfadeleri

Perl, düzenli ifadelerin ilk kullanıldığı programlama dillerden biri olarak, çok gelişmiş regular expression kütüphanelerinden birine sahiptir ve kendine özgü bir sözdizimi mevcuttur. POSIX standartlarından bir miktar farklılık gösteren bu türün ayrıntılarına bu yazıda değinmeyeceğiz.

Karakter Sınıfları

Düzenli ifadelerin kullanımında, [A-Za-z0-9] gibi karmaşık görünümlü parçaları sadeleştirebilmek için, bazı öntanımlı karakter grupları yaratılmıştır. POSIX standardında veya bazı özel uygulamalarda tanımlı önemli karakter gruplarını ve karşılıklarını aşağıda bulabilirsiniz:

İfade POSIX Sınıfı Karşılık Açıklama
\w [[:alnum:]] [A-Za-z0-9] Alfanümerik karakterler (harfler ve rakamlar)
\W [^A-Za-z0-9] Alfanümerik olmayan karakter
\d [[:digit:]] [0-9] Rakamlar
\D [^0-9] Rakam olmayan karakter
\s [[:space:]] [ \t\n\r\v\f] Beyaz (whitespace) karakterler
\S [^ \t\n\r\v\f] Beyaz (whitespace) olmayan karakter
[[:alpha:]] [A-Za-z] Harfler
[[:lower:]] [a-z] Küçük harfler (karşılık gelen ifade, karakter kodlamasına göre farklılık gösterebilir)
[[:upper:]] [A-Z] Büyük harfler (karşılık gelen ifade, karakter kodlamasına göre farklılık gösterebilir)
[[:punct:]] Noktalama işaretleri
\A ^ Metin başlangıcı
\Z $ Metin sonu
\b Sözcük sınırı
\B Sözcük sınırı dışında konum

Metin Ayrıştırma

Düzenli ifadeleri, metin eşleme (pattern matching) ve doğrulama (validation) amaçları dışında, metin ayrıştırma (parsing) ve değiştirme (replacement) amaçları için de kullanabiliriz. Bunun için, Regular Expression sözdiziminde \ karakterinden sonra gelen bir rakam, daha önce geçmiş bir metin parçasına referans olarak kullanılır (back reference). Metin parçaları olarak da parantez içindeki gruplar alınır (capturing group). Örneğin, aşağıdaki metni ele alalım:

color:black

Bu metinle eşleşmesi için şöyle bir regular expression tanımlarsak:

(.*):(.*)

Eşleşen ifadede \1, "color" parçasına, \2 de "black" parçasına karşılık gelir. \0 ifadesi de eşleşen metnin tümünü göstermek için kullanılır. Buna göre regular expressin desteği bulunan bir metin editöründe "aranan metin" kısmına (.*):(.*), "değiştirilecek metin" kısmına \1=\2 yazarak "color=black" metnini; \2:\1 yazarak da "black:color" metnini elde ederiz.

\1 gibi ifadeleri değiştirme metinlerinin içinde kullanabileceğimiz gibi, düzenli ifadelerin kendisinde de kullanabiliriz. Örneğin, bir metin içindeki pekiştirme sıfatlarını bulmak istiyorsak, aşağıdaki gibi bir regular expression yazabiliriz:

\b(\w{2})[prsm]\1\w*\b

Bu ifade, kelime sınırları (\b) içinde önce iki karakter arar (\w{2}), üçüncü karakter olarak p, r, s, m harflerinden biriyle ([prsm]) ve sonra ilk iki eşleştiği harfle tekrar eşleşmeye çalışır. (\1 ifadesi, önceki paranteze referanstır) Daha sonra da kelime, farklı harflerle devam eder. Bu açıklamamıza göre ifade, "masmavi", "yemyeşil", "sapsarı" benzeri sözcüklerle eşleşecektir.

Sık Kullanılan Kalıplar

Bu bölümde sık kullanılan düzenli ifade kalıplarından ikisini detaylı bir şekilde açıkladıktan sonra, birkaç tane de örneği detaylarına inmeden sizinle paylaşacağız.

IP Adresi

Bilgisayarların İnternet'teki konumunu belirten IP adresleri, 144.122.199.90 formatında, nokta karakterleriyle ayrılmış dört sayıdan oluşurlar. Her bir sayı, 0'dan 255'e kadar değer alabilir. Bunları bilerek, bir metin içindeki IP adreslerini bulmaya yarayan bir regular expression yazmak istiyoruz. İlk olarak, aşağıdaki gibi bir düzenli ifade geliştirebiliriz.

\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b

Bu ifade, bir kelime halinde bulunan (\b ifadeleri kelime sınırını belirtir) IP adreslerini bulmaya çalışır. Her (\d{1,3}) ifadesi 1-3 rakamlı sayılarla, aralardaki \. ifadeleri de noktalarla eşleşeceğinden bu regular expression, tüm IP adresleriyle eşleşecektir. Düzenli ifadelerde nokta, normalde özel bir anlam ifade ettiğinden, gerçekten nokta karakterini belirtmek istediğimizde başına bir \ karakteri koymaya dikkat etmemiz gerekir.

Yazdığımız düzenli ifade, istediğimiz gibi tüm düzenli ifadelerle eşleşiyor; ancak 305.324.564.987 gibi 256'dan büyük sayılar içerdiğinden IP adresi olamayacak kelimelerle de eşleşir. Sayıların 0-255 aralığında olduğunu, kullandığımız programlama dilinde \1 \4 gibi referanslarla kontrol edebileceğimiz gibi, düzenli ifadenin kendisinde de yapabiliriz. Bunun için \d{1,3} parçalarını (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) benzeri parçalarla değiştirmemiz gerekir.

E-Posta Adresi

E-Posta adreslerini bulan/doğrulayan bir düzenli ifade yazmak dikkat gerektiren bir iştir. Çünkü içinde pek çok kural barındırır. Bu kurallar, RFC 822 denilen bir standartta belirtilmiştir ve hepsini kontrol etmek çok zordur. Bu nedenle, e-posta kontrolü yapan bir düzenli ifade yazıyorsak, mümkün olduğu kadar düzenli ifadeyi karışık hale getirmeden, tüm mail adresleri ile eşleşmeyecek olsa da en azından %99 doğruluk oranına sahip bir regular expression yazmamız gerekir.

Aşağıdaki ifade, basitçe bir metnin e-posta adresi belirtip belirtmediğini kontrol etmek için bir @ işareti, bunun solunda bir kullanıcı adı, sağında da noktalarla ayrılmış bir alanadı bulmaya çalışır.

^([\w.-_+%]+)@([\w.-]+\.[\w]{2,6})$

Bu ifade, pek çok e-posta adresi için doğru sonuç verir. Ancak, "murat@144.122.199.90" benzeri IP içeren e-posta adresleri ile eşleşmez. Ayrıca, "user@domain....com" gibi birden fazla noktalama işaretinin yanyana gelip gelmediğini, kullanıcı adının sembolle değil harfle başladığını ve daha pek çok ayrıntıyı kontrol etmez. Bundan daha detaylı ve doğruluk oranı daha yüksek bir regular expression istiyorsak, örneğin IP adresli e-posta adreslerini de kabul etmek istiyorsak, aşağıdaki ifadeyi kullanabiliriz:

^([\w.-_+%]+)@((\[([0-9]{1,3}\.){3}[0-9]{1,3}\])|(([\w-]+\.)+)([a-zA-Z]{2,6}))$

Eğer %100'e en yakın bir doğruluk oranına ihtiyacımız varsa, burdaki gibi çok daha uzun bir regular expression kullanabiliriz. Ancak, bu kadar ayrıntılı bir ifade yerine, ihtiyacımız olan doğruluk oranını belirleyip, buna uygun bir ifade kullanmamız en iyisidir.

Unutmamalıyız ki bir metin, RFC 822 denilen standarta uysa bile gerçek bir mail adresi belirtmeyebilir. Örneğin, qwe@asd.zxc adresinin sonunda geçen ve top-level domain address denen zxc kısmı, şu an dünyada İnternet adreslerini yöneten ICANN tarafından tanımlanmış bir top-level domain değildir. Yani bu kısmı da geçerli adresler listesinden kontrol etmek gerekebilir. Ne var ki, bu liste de (.tv, .com, .net, .org, .info, .museum, .tr, .uk, ...) sürekli değişmektedir. Bunun yanında, e-posta adresinin son kısmını doğrulasak bile, gerçekten öyle bir alan adı ve o alanadı altında öyle bir e-posta hesabı olduğundan emin olamayız. Yani, bir e-posta adresini, yalnızca gerçekten e-posta göndererek doğrulayabiliriz. Tabii ki bunu düzenli ifadeleri kullanarak yapmak mümkün değildir.

Diğer Örnekler

TürİfadeEşleşen İfadeler
URL ^((https?)|(ftp))\://([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$ http://e-bergi.com
https://www.cclub.metu.edu.tr
ftp://ftp.linux.org.tr
Telefon Numarası ^[+]90[-]\d{3}[-]\d{3}[-]\d{4}$ +90-555-123-4567
+90-232-000-9999
Tarih ^(0[1-9]|[12][0-9]|3[01])\.(0[1-9]|1[012])\.(19|20)\d\d$ 01.08.2008
18.05.1987
31.02.1900
Saat ^([01][0-9]|2[0-3]):([0-5][0-9])$ 00:00
23:59

Sonuç

Üç yazı boyunca, sizlere düzenli ifadeler (regular expressions) hakkında olabildiğince detaylı ve pratik bilgiler vermeye çalıştık. Metin arama, değiştirme, doğrulama, ayrıştırma gibi alanlarda kullanılan düzenli ifadelerin sizin de hayatınızı kolaylaştırmasını ve e-bergi'yi bir kez daha hatırlamanızı sağlamasını umarız. İyi günler..

Önceki Yazılar

Kaynaklar



Murat Ongan
- 8 -