|
:: Düzenli İfadeler, Giriş
Giriş, Düzenli İfadeler Yeniden merhaba. Bu yazımızın konusu düzenli ifadeler. Diğer adıyla Regular Expressions. Peki nedir bu düzenli ifadeler ve ne işe yararlar?
Özetle onun için bir ifade üzerinde işlem yapmaya yarayan bir tür söz dizimi diyebiliriz. Ve bu işlemler arasında (kullandığınız fonksiyona bağlı olarak) arama, değiştirme ve parçalamayı sayabiliriz.
Ama hemen belirtelim aslında bu bahsettiğimiz söz diziminin de (en azından PHP için) birden fazla standardı ve o standarda uygun fonksiyon grubu vardır. Biz ise yazımızda bu standartlar arasında (bence) en güçlü olanını yani PCRE (Perl-Compatible Regular Expressions/Perl-Uyumlu Düzenli İfadeler) 'i ele alacağız. Ve bu nedenle PHP'nin perl uyumlu düzenli ifade fonksiyonlarını kullanacağız. Daha sonra bunlara uzun uzun değinecek olsak da şimdilik kısa bir özet yapalım.
preg_match_all: İfadedeki tüm uyumlu bölümleri verir. (evrensel arama) preg_match: İfadedeki uyumlu bölümü verir. (arama) preg_replace: İfadedeki uyumlu bölümleri verilenlerle değiştirir. (değiştirme) preg_replace_callback: İfadedeki uyumlu bölümleri değiştirmek üzere verir. (arama ve değiştirme) preg_split: İfadeyi parçalar. preg_grep: Dizi içindeki desene uyumlu indeksleri çevirir. preg_quote: İfadedeki düzenli ifade anahtarlarını etkisizleştirir.
Aslında yapmak istediğim size yapabilecekleriniz üzerine ufak bir ipucu verebilmek. Ki gördüğünüz gibi düzenli ifadeler ve ona bağlı fonksiyonlar, ifadeler üzerinde denetim sağlamak içindir. Bunun içinde size verilen ifadeyi filtrelemekten tutunda, yeni bir sözdizimi geliştirmeye kadar her şeyi sayabilirsiniz.
Şimdi başlamadan önce bazı taşları yerine oturtmak adına basit bir örnek verelim.
<? if(!preg_match("'^\w+@\w+\.\w[\w\.]+$'",$email)) echo "Hatalı mail adresi !"; ?>
Yukarıdaki kısa kod tahmin edebileceğiniz gibi $email içeriğindeki ifadenin gerçek bir mail adresi olup olmadığını kontrol eder. Bunun için bir düzenli ifade fonksiyonu olan preg_match'ı kullanır ve bu fonksiyon üç parametre alır. İlki düzenli ifade deseni. Örneğimizde bu '^\w+@\w+\.\w+$' ifadesi. İkincisi aranan ifade. Yine örneğimizde bu $email değişkeninin içeriği. Son olarak bizim örneğimizde yer vermediğimiz (ama daha sonra değineceğimiz) sonuç dizisi.
Bunları açıkladıktan sonra artık fonksiyonun kendisine parametre olarak verilen düzenli ifade desenini, ifadeye uygulayarak eğer uyum sağlanırsa (yani aradığını bulduysa) doğru (true), sağlanmaz ise yanlış (false) döndürdüğünü söyleyebiliriz.
Özetle bizim bu yazıda üzerinde duracağımız şeyde örnekteki '^\w+@\w+\.\w[\w\.]+$' türünden ifadeleri nasıl yazabileceğiniz.
Düzenli İfade Desenleri Hemen örneğimize geri dönelim.
'^\w+@\w+\.\w[\w\.]+$'
İfadenin (şuan için) anlaşılmaz olduğunun farkındayım. Ama ilerleyen bölümlerde aslında çok basit bir söz dizimi olduğunu göreceksiniz. Söz dizimi yani birden fazla ifadenin belirli bir düzende bir araya getirilmesi. İşte biz buna düzenli ifadelerde desen diyeceğiz. Yani yukarda gördüğümüz şey aslında bir düzenli ifade deseni.
Şimdi bu desene tekrar baktığımızda bazı noktalama işaretleri ve harflerden oluştuğunu görebiliriz. İşte bu karakterler arasında özel bir anlamı yada görevi olanlara da desen atomu yada daha anlaşılır bir dille anahtar diyebiliriz. Şimdi her şey tamamsa sanırım artık bu anahtarların neler olduğuna ve hangi görevleri üstlendiklerine geçebiliriz.
Ama ondan önce desenin sağındaki ve solundaki ' tırnakların birer desen sınırlayıcısı olduğunu ve bu konuya daha sonra ayrıntılarıyla değineceğimizi belirtmem gerekiyor. Yani şimdilik bilmemiz gereken bizim desenden kastımızın aslında tırnakların arasında kalan ^\w+@\w+\.\w[\w\.]+$ bölümünün olduğu.
Özetle bu sınırlayıcıların desenin etrafına yerleştirilmesinin bir tür zorunluluk olduğunu söylemeliyim. Ama biz verdiğimiz örneklerde bu sınırlayıcılara şimdilik yer vermeyeceğiz. Yinede siz örnekleri test ederken deseni sınırlayıcılar içine almayı unutmayın.
Şimdi kaldığımız yerden yani anahtarlardan devam edebiliriz.
Desen Atomları (Anahtarlar) İsterseniz işe anahtarları kısaca tanımlayarak başlayalım.. Daha sonrada bu anahtarların her birini ayrı ayrı ele alırız. İlk olarak genel anahtarlara göz atalım:
\ Genel kaçış karakteri (buna daha sonra değineceğiz) ^ İfadenin (yada satırın) başlangıcı $ İfadenin (yada satırın) sonu . Herhangi bir karakter [ Karakter sınıfı başlangıcı ] Karakter sınıfı sonu | Alternatif bölümün başlangıcı (yada kısaca "or" deyimi) ( Alt desen (subpattern) başlangıcı ) Alt desen (subpattern) sonu ? 0 yada 1 miktar (yanı ya hiç olmayan yada varsa sadece "bir" tane olan) * 0 yada daha fazla miktar + 1 yada daha fazla miktar { Maksimum/Minimum miktar başlangıcı } Maksimum/Minimum miktar sonu
Sadece karakter sınıfları (buna daha sonra değineceğiz) içinde kullanabileceğiniz anahtarlar ise şöyle:
^ Sınıfı olumsuzlaştıran tanımlayıcı. (örneğin a dışında her şey için [^a] gibi) - Başlangıç/bitiş aralığı (örneğin 0'dan 9'a kadar demek için [0-9] gibi)
Bunların dışında birde, birden çok görevi olan özel bir anahtar daha, \ ters bölü (backslash) anahtarı var ki biz onun her görevini teker teker ele alacağız.
Genel kaçış karakteri olarak \ ters bölü anahtarı
Bu kullanım yukarıda listesini verdiğimiz anahtarları sadece bir karakter olarak betimleyebilmek içindir. Örneğin biz metin içinde . nokta karakterini arıyor olalım. Oysa listede de görebileceğimiz gibi bu karakterin düzenli ifadeler içinde özel bir anlamı var. Yani o bir desen anahtarı. Öyleyse istediğimizi elde edebilmek için karakterin başına, genel kaçış anahtarı olarak \ ters bölüyü eklememiz gerekiyor. Örneğin,
\.
deseni gibi. Ama aradığımız zaten bu genel kaçış anahtarının kendisi ise bu kez onunda başına bir genel kaçış anahtarı yerleştirmemiz lazım.
\\
Not: \ karakteri bildiğiniz gibi PHP içinde kaçışı temsil eder. Yani yukarıdaki deseni test ederken deseni "'\\'" şeklinde değil "'\\\'" şeklinde yazmalısınız. Aksi halde sağdaki sınırlayıcı yani ' tek tırnak etkisizleşecek ve hata alacaksınız.
Görünmez (non-printing) karakterleri yakalamak
Bunu da şöyle izah edebiliriz. Örneğin bir metin yazarken satır sonuna geldiğinizde enter tuşuna bastınız ve alt satıra geçtiniz. Oysa görünürde o noktaya basılmış herhangi bir karakter yoktur. Aslında tam tersine sizin metin editörünüz o noktaya sizin göremeyeceğiniz bir karakteri yani yeni satır (newline) karakterini basmıştır. İşte aşağıda listesini verdiğimiz buna benzer görünmez karakterleri yakalamak için yine \ ters bölü anahtarını kullanmalısınız.
\n Herhangi bir yeni satır karakteri \r Herhangi bir satır başı (carriage return) karakteri \t Herhangi bir girinti (tab) karakteri \f Herhangi bir kağıt çıktısı (formfeed) karakteri \cx Herhangi bir CTRL-X karakteri (CTRL+C yada CTRL+V gibi) \xFF Herhangi bir onaltılık (hexedecimal) kod. (© demek için \xA9 gibi)
Karakter setleri
Liste yine aşağıda. Ki listeden de anlayabileceğiniz gibi \ ters bölü anahtarı ile kullanacağınız bu karakterler, bazı karakter setlerini (rakam,harf yada beyaz boşluk gibi) betimlemek için kullanılır.
\d Herhangi bir ondalık sayı karakteri (0,1,2,3 vb. gibi) \D Ondalık sayı karakteri dışında herhangi bir karakter \s Herhangi bir beyaz boşluk (whitespace) karakteri (\r,\n,\t,\f vb. gibi) \S Beyaz boşluk dışında herhangi bir karakter \w Herhangi bir yazı karakteri. (a'dan z'ye ve 0'dan 9'a tüm harf, rakam ve _ karakteri) \W Yazı karakteri dışında herhangi bir karakter.
Sınırları yakalamak
\ ters bölü anahtarının son kullanım şekliyse bir ifade yada kelimenin sınırlarını belirlemek için olanıdır. Ama bu kullanımın diğerlerinden belirgin bir farklılığı vardır. Oda aşağıda listesi verilen karakterlerin aslında fiziksel bir karşılıklarının olmayışı.
\b Kelime sınırı (karakteri) \B Kelime sınırı hariç herhangi bir karakter \A İfadenin başı (karakteri) \Z İfadenin yada satırın sonu (karakteri) \z İfadenin sonu (karakteri)
Bunu şöyle anlayabiliriz. Örneğin bir iki üç ifadesindeki her kelimenin sınırları bellidir. Oysa ifadede bu sınırları belirleyen özel bir karakter mevcut değildir. Bizse sadece kelime aralarındaki boşluk, satır sonu, nokta gibi karakterlere bakarak kelimenin nerde bittiğini belirleriz. Benzer şekilde yazdığınız desenler içinde kullanacağınız \b anahtarı da sınırı bu şekilde belirler. Ama ifadede \b anahtarının gerçek, fiziksel bir karşılığı yoktur. Çünkü örneğin bir kelimesi ve ardından gelen boşluk karakterinin arasında gerçekte bir karakter olması zaten mümkün değildir.
Birde şimdilik yer vermediğimiz bir kullanım daha var (backreferance). Ama onu da daha sonra açıklamak üzere bir kenara bırakarak artık bu denli çok anahtarın nerde ve nasıl kullanılacağına bakalım.
Düzenli İfade Desenleri (Söz Dizimi) Karakterler Yani ifadedeki bir yada birden fazla karakteri yakalamak için yazacağımız desenler. Örneğin,
ali
deseni ali, mehmetali, alikemal ifadelerinin tümünü yakalar. Ama aradığımız karakter eğer yukarda listesini verdiğimiz anahtarlardan biriyse bu kez karakterin başına genel kaçış anahtarı olarak \ ters bölüyü yerleştirmeliyiz.
\d\.
gibi. Bu desen 1 ile 9 arasındaki tüm .incileri yakalamak içindir. Örneğin 1., 2., 3. gibi (çünkü başta belirttiğimiz gibi \d özel bir anahtar (yada karakter setidir) ve 0 ile 9 arasındaki tüm rakamları kapsar). Benzer şekilde aradığımız karakter kaçış karakterinin kendisiyse bu kez onunda başına bir kaçış yerleştirmeliyiz.
\w:\\
Yine yukarıdaki desen A'dan Z'ye tüm sürücü isimlerini yakalamak içindir. Örneğin A:\, C:\, D:\ gibi (Burada da yine a'dan z'ye harfleri barındıran \w karakter setini kullandık).
Son olarak . nokta anahtarını yazdığınız desende herhangi bir karakteri betimlemek için kullanabilirsiniz. Yani,
<.>
deseni <1> ifadesini yakalayabildiği gibi <a> ifadesini de yakalar. (Sadece harf ve rakamları değil, . nokta anahtarı yeni satır (\n) ve satır başı (\r) hariç herhangi bir karakteri betimleyebilir.)
Karakter Sınıfları Ama bazı durumlarda bu yeterli olmayabilir. Örneğin tarih yada zaman yazılımlarında,
\d\d
deseni 00'dan 99'a kadar tüm iki haneli sayıları yakalayacağından maksimum 59 olabilen dakika ve saniyeleri yakalamak için kullanılamaz. Bunun yerine,
[012345]\d
Şeklinde bir desen yazmanız lazım. Böylece ilk hane mutlaka [ ve ] anahtarları arasında kalan karakterlerden biri olmaya zorlanacak ve istediğimiz olacaktır. Aynı deseni bir başka şekilde yani
[0-5]\d
şeklinde de yazabiliriz. Buda aralık belirlemeye iyi bir örnek sanırım. Son olarak ^ kesme anahtarını yani karakter sınıfını olumsuzlaştıran anahtarı ele alırsak
[^üğişöç]
Deseni türkçeye özgü harfler dışında herhangi bir karakteri bulmak için kullanılabilir.
Sınırları betimlemek Bunu anlatabilmek için de ilk verdiğimiz örneğe geri dönelim.
ali
Demiştik ki bu desen ali, mehmetali, alikemal ifadelerinin tümünü yakalar. Ama biz bunu istemiyor olabiliriz. Örneğin istediğimiz sadece başında ali geçen ifadeleri yakalamak ise
^ali
demeliyiz. Bu durumda mehmetali ifadesi elenecektir. Yada biz sonunda ali geçen ifadeleri arıyorsak eğer
ali$
desenini yazmamız gerekir. Böylece alikemal ifadesinin yakalanmasını engelleriz. Son olarak biz sadece ali ifadesini arıyorsak
^ali$
demeliyiz. Böylece hem mehmetali hemde alikemal elenir ve biz sadece ali ifadesini yakalarız. Gördüğünüz gibi ^ kesme ve $ dolar anahtarları, tüm desenin ifadenin hangi kısmına uygulanacağını belirler. Bunun dışında benzer bir görevi olan bir anahtar daha vardır.
\bali\b
Bu deseni bir önceki ^ali$ deseni ile karşılaştırırsak, ^ali$ deseni mehmet ali ifadesini de yakalayamaz. Çünkü ifadede ali sonda olsa da başta mehmet vardır. Oysa \bali\b aynı ifadedeki ali 'yi yakalar. Çünkü \b anahtarı kelime sınırını belirten özel bir anahtardır. (Buna daha öncede değindik).
Başka bir örnekle durumu aydınlatmaya çalışırsak,
.\b
deseni ali ifadesindeki i 'yi bulur. Çünkü desene göre bizim istediğimiz kelimenin sonundaki herhangi bir (sadece bir adet) karakterdir. Devam edersek, bu özel anahtarın zıttını yani \B anahtarını şöyle örneklendirebiliriz.
.\B
Bu desende en az iki karakterli kelimelerin ilk karakterini verir. Örneğin a li ifadesindeki l 'yi. (Çünkü ilk kelime yani a tek karakterli olduğu için desen onun ardından gelen li 'ye yönelir)
Not: Aslında hepsi bu kadar değil. Daha henüz değinmediğimiz \A, \Z, \z özel anahtarları da vardır. Ama bunlar daha sonra değineceğimiz desen düzenleyiciler ile ilişkili olduğundan şimdilik es geçmek zorundayız.
Alternatif yollar Bazı ifadelerin çeşitli yazılışları olduğunu biliriz. Onları yakalamak içinse alternatif yollar belirlememiz gerekir. İşte desen yazarken alternatif yollar belirlemenizi sağlayan özel karakter | boru (pipe) karakterdir.
erkek|kadın
Bu desen hem erkek hem de kadın ifadelerini yakalar. Ama tahmin edersiniz ki erkek kadın ifadesini yakalamayacaktır.
Not: Şimdilik kısaca değiniyoruz ama ilerleyen bölümlerde bu konuya sıkça geri dönüceğiz.
Miktarı belirlemek Bu belki de en önemli konulardan biri. Çünkü bu sayede yazdığınız desenlerde yüksek bir denetim sağlayabilirsiniz. O yüzden hemen bunun için kullanabileceğiniz ön tanımlı üç anahtar ve miktar sınırlarını belirleyebileceğiniz özel bir kullanım olduğunu söyleyip bunları açıklamaya girişelim.
? soru işareti
Sıfır yada bir tane anlamına gelir. Yani aradığımız desen hiç olmayabilir. Ama olursa da ondan en fazla bir tane istiyoruz demektir. Örneğin,
\d?
deseni, en az bir haneli tüm sayıların en soldaki hanesini verir. Yani sayı 0 ise 0'ı 99 ise 9'u.
Artı +
En az bir yada birden fazla anlamına gelir. Yani aradığınız desenden en azından bir tane olmalı. Ama birden fazlada olabilir. Örneğin,
\d+
deseni, yukarıdakinden farklı olarak üst limit olmaksızın en az bir haneli tüm sayıları yakalamak içindir. Bunun anlamı bu desenle sıfırdan sonsuza tüm sayıları yakalayabilirsiniz.
Yıldız *
Sıfır yada birden fazla anlamına gelir. Yani aradığınız ifade hiç olmayabilir. Ama birden fazlada bulunabilir. Örneğin,
\d*
deseni, yine üst limit olmaksızın tüm sayıları yakalayacaktır. Yani sayı hiç olmayabilir ama olursa da kaç haneli olduğu önemli değil.
Süslü parantezler {x,y}
Ama bu yetmeyebilir. Bazı durumlarda alt ve üst limitleri de siz belirlemek isteyebilirsiniz. Bu durumda süslü parantezleri kullanmanız gerekir.
\w{2,4}
Örneğin yukarıdaki desen en az iki en çok dört yazı karakterini yakalayacaktır. Başka bir şekilde ifade edersek süslü parantezler içinde virgülden önce yazdığınız ilk rakam miktarın alt sınırını, virgülden sonra yazdığınız ikinci rakam ise miktarın üst sınırını betimler. Eğer üst sınır belirtmek istemiyorsanız yazmak zorunda değilsiniz. Örneğin,
\w{2,}
gibi. Bu kez üst limit olmaksızın en az iki yazı karakterini yakalayabilirsiniz. Ama istediğiniz ifadeden sadece belirttiğiniz miktarda elde etmekse üst sınırı tamamen kaldırmanız gerekmekte.
\w{2}
Aslında aradaki farkı daha iyi anlamak için şu tabloya da göz atabilirsiniz.
İfade | Desen | Karşılık -------+---------+---------- a | \w{2,4} | - abcdef | \w{2,4} | abcd a | \w{2,} | - abcdef | \w{2,} | abcdef a | \w{2} | - abcdef | \w{2} | ab
Not: Tabloda - ifadenin desenle uyumsuz olduğunu gösterir. Yani desen ifadenin hiç bir bölümünü yakalamamıştır.
Açgözlü ve tembel desenler Komik geldiğini biliyorum. Ama bu konu için daha iyi bir başlıkta düşünemiyorum. Hemen ne anlatmak istediğimize geçersek. Örneğin biz <a> <.> gibi bir ifadede <> içinde kalan olası tüm karakterleri yakalamak istiyorsak
<.+>
gibi bir desen yazabiliriz. Ama desenimiz bu durumda aç gözlü davranarak bize en soldaki < ve en sağdaki > karakterleri arasındaki tüm karakterleri verecektir. İşte biz bu durumun önüne geçmek için desenimizi tembelleştirmeliyiz.
<.+?>
Gördüğünüz gibi bunun için tek yapmamız gereken miktar anahtarının sonuna bir ? anahtarı daha eklemek. Böylece desen sağındaki ilk > karakterini gördüğü an duracak ve bize istediğimizi verecektir.
Aslında tüm diğer miktar anahtarları içinde aynı şey geçerlidir.
Tembel soru işareti ??
Diğeriyle arasındaki farkı anlamak için 123 ifadesini ele alalım. \d?\d deseni bu ifadenin 12 bölümünü alacaktır. Ama \d??\d deseni ifadenin yalnızca en soldaki hanesini alır. Yani 1'i. Çünkü ?? aç gözlü davranmayıp ilk \d anahtarına karşılık gelen karakteri bulduğu an duracaktır.
Tembel artı +?
Yine aradaki farkı anlamak için ilk verdiğimiz <a> <.> örneğini ele alabiliriz. Tekrar etmek gerekirse <.+?> deseni bu ifadede sağdaki ilk > karakterini gördüğü an duracaktır. Daha doğrusu desenle uyumlu ilk karşılık olan <a> ifadesini bulduğu an daha fazla devam etmeyecek ve bulduğu karşılığı geri çevirecektir.
Tembel yıldız *?
Aynı örnekten devam edersek <.+?> deseni ile <.*?> deseni arasındaki fark <.*?> deseninin ifadede <> bölümü arasında hiç bir şey olmasa dahi bölümü yakalamasıdır. Oysa ifadeyi <> <.> şeklinde değiştirdiğimizde <.+?> deseni ifadenin <> <.> bölümünü yakalayacaktır.
Çünkü daha öncede söylediğimiz gibi + anahtarı kendisinden önce gelen ifadeden en az bir tane bulmaya şartlanmıştır.
Tembel süslü parantezler {x,y}?
Son olarak pratikte nasıl bir yararı olduğunu kestiremesem de miktar sınırlarınızı tembelleştirmenizde mümkün. Aradaki farklı görebilmek için yine aşağıdaki tabloya göz atabilirsiniz.
İfade | Desen | Karşılık -------+----------+---------- abcdef | \w{2,4} | abcd abcdef | \w{2,4}? | ab abcdef | \w{2,} | abcdef abcdef | \w{2,}? | ab abcdef | \w{2} | ab abcdef | \w{2}? | ab
Özetle miktar sınırlarınızı tembelleştirmek demek miktarın üst sınırını göz ardı etmeye benziyor. Yani ne olursa olsun alt sınır olarak belirlediğiniz miktarda tekrar elde edebiliyorsunuz.
Son olarak söylemek istediğim düzenli ifadele desenlerinizin, daha doğrusu miktar anahtarlarının ön tanımlı olarak tembel olmadığıdır. Ama bu daha sonra değineceğimiz desen düzenleyicileri ile değiştirilebilir. Bu durumda aklınızda tutmanız gereken aslında miktar anahtarlarının ardından gelen ? anahtarının o anki durumu tersine çevirdiğidir.
Yani açgözlü ise tembel, tembel ise açgözlü gibi. Bu konuya da daha sonra geri döneceğiz.
Bu bölümün sonunda... Artık ilk verdiğimiz örneği daha iyi anlayabiliyoruz öyle değil mi?. Ona geri dönersek,
^\w+@\w+\.\w[\w\.]+$
aslında bu desenin tam olarak, en az bir yazı karakteri ardından @ karakteri, onun ardından yine en az bir yazı karakteri nokta ve yine en az bir yazı karakteri ve/veya nokta aradığını görebiliriz. Yani aradığı eksiksiz bir mail adresidir.
İşte buda aslında ziyaretçilerimizin bize hatalı posta adresi vermelerini engelleyecek basit bir filtreden başka bir şey değildir. Son olarak umarım bu bölümde size düzenli ifadeler ile ilgili temel bilgileri aktarabilmişimdir.
Çünkü bundan sonraki bölümde artık bir parça daha karmaşık konulara değineceğiz.
| Rating : 10 üzerinden 8.88 |
|
|
|