Düzenli İfadeler - Tanım ve Fonksiyonlar
Cum 01 Eylül 2023Düzenli İfadeler (Regular Expressions)
Düzenli ifadeler (RegEx ya da Regular Expression), bir karakter dizisi içinde bulunan, belli bir düzene uyan eşleşmeleri bulmanıza ve yönetmenize yardımcı olacak desenler oluşturmanıza izin veren bir metin dizisidir (bir string ifadedir). Bir metinde geçen karakterleri RegEx desenleri (pattern) kullanarak arayabiliriz.
Düzenli ifadeler bir arama işleminde eşleştirilecek deseni temsil eden özel dizelerdir. Java ve Perl gibi programlama dillerinden grep, sed ve metin düzenleyici vim gibi metin işleme araçlarına kadar çok çeşitli bilgi işlem uygulamalarında kullanılan önemli bir araçtır.
Düzenli ifadeler sayesinde ne yapabiliriz bir bakalım :
-
Yer değiştirme: Belirli parçaları değiştirir. Örnek olarak metin içerisindeki tüm büyük harfleri küçük harf ya da tüm küçük harfleri büyük harfe dönüştürebilirsiniz.
-
Doğrulama : Yazdığınız bir program olsun, bu programda büyük veya küçük harf, nokta veya rakam gibi kriterleri karşılayıp karşılamadığını kontrol edebilirsiniz.
-
Arama : Bir metin içerisindeki tüm ögeleri aratabilirsiniz. Örneğin telefon numaraları ya da e-posta adresleri gibi.
-
Koordinat ile hareket etme : Örneğin, bir dizindeki belirli paketleri, dosyaları işlemek isteyebilirsiniz ancak yalnızca belirli koşulları karşılıyorlarsa, komut satırında çalıştırabiliyorsunuz.
-
Metni Yeniden Biçimlendirme : Örneğin, Bir programdaki verileri metin dosyası olarak dışa aktarabilir, ardından düzenini değiştirerek metin düzenleyicisi kullanarak başka bir programa aktarabilirsiniz.
Python’daki düzenli ifadelere ilişkin her şey, bir modül içinde tutulur. Bu modülün adı re'dir. re modülü Python'ın dahili (build-in) modeüllerindendir. Ayrıca yüklememize gerek yoktur. Bilgisayarınızda Python yüklü ise, re modülü de yüklenmiş ve kullanıma hazır olarak bekliyordur.
Düzenli ifadeleri kullanabilmemiz için öncelikle bu re modülünü içe aktarmamız gerekir:
import re
RegEx Fonksiyonları / Metotları
re modülü bir veride geçen karakterleri bulmamıza ve değiştirmemize olanak sağlayan bazı fonksiyonlara sahiptir. Bunlar;
Fonksiyon / Metot | Açıklama |
---|---|
match() | Karakter dizisinin başında eşleşme olup olmadığını göster |
search() | Eşleşme olup olmadığını göster |
findall() | Tüm eşleşmeleri göster (liste halinde) |
finditer() | Belirtilen desenlerin karakter dizileri içerisindeki konumunu bulmak için kullanabiliriz. |
split() | Eşleşme noktalarından böl ve liste oluştur |
sub() | Eşleşmeleri verilen ifade ile değiştir |
match() Metodu
Bir karakter dizisi başında belirli bir kelimenin ya da kelime grubunun geçip geçmediğini öğrenmek istiyorsak bu işlemi match()
metodunu kullanarak yapabiliriz.
match() metodunun;
- ilk argümanı eşleştirilecek (aranacak) değer, yani desen
- ikinci argümanı ise, aramanın yapılacağı karakter dizisi olmalıdır.
cumle = "python güçlü bir programlama dilidir."
re.match(r"python", cumle)
<re.Match object; span=(0, 6), match='python'>
Yukarıdaki r"python"
deseni, yani aranan değer, zaten bulunan değeri ile aynı olduğu için, bu arama/eşleşme yönteminin kullanımı size anlamsız/saçma gelmiş olabilir.
İlerleyen bölümlerde Metakarakterler konusunu anlatılırken çok farklı desenler oluştururarak farklı sonuçlar bulacağız. O bölümde düzenli ifadelerin ne için kullanılabileceğini ve bizlere ne kadar kolaylık sağladığını anlayacaksınız.
Düzenli ifadeler için bir desen tanımlarken, r
ifadesini yazmamız istenir. İlerleyen bölümlerde göreceğimiz üzere, özel metakarakterleri desen içerisinde yazmamız gerektiğinde r
ifadesini kullanmadığımızda sorun ile karşılaşırız. Özel metakarakterleri desen içerisinde yazmadığımız zaman da r
ifadesini kullanmak sorun çıkarmıyor. Üstelik r
ile tanımlanan ifade, bir değişken mi? yoksa bir düzenli ifade deseni mi? olduğu kodu inceleyenlere yardımcı oluyor.
match
nesnesinin dönen değeri ile bazı işlemler yapılabilir. Bunun için nesnenin yöntemleri kullanılır. Bunlar;
span()
Başlangıç ve bitiş konumlarını tuple olarak verir.string
Dönen metinsel dizgi ifadesini (string) yazdırırgroup()
Eşleşen grupları yazdırır. (group(1)
,group(2)
, ...vb.group(0) = group()
eşittir, tüm eşleşmeleri yazdırır.)
Bu çıktıdaki span parametresi bize, aradığımız python karakter dizisinin (yani desenin), cumle değişkeninin 0. ile 6. karakterleri arasında yer aldığını söylüyor.
span() metodu
Eşleşmenin başladığı ve sona erdiği karakterlerin sırasını verir (0 değeri 1. karakteri temsil eder):
cumle = "python güçlü bir programlama dilidir."
x = re.match("python", cumle)
x.span()
(0, 6)
match()
metodunun span()
metodunu kullanarak değerleri doğrudan elde edebiliriz.
cumle[0:6]
'python'
farklı tarzda yazmak istersek, şu ifadeyi de kullanabiliriz;
cumle[x.span()[0]:x.span()[1]]
'python'
txt = "Bu sabah yağmur var İstanbul'da..."
a = re.search(r"\by\w+", txt)
print(a.span())
(9, 15)
string Özelliği
Eşleşme bulunan metni gösterir.
txt = "Bu sabah yağmur var İstanbul'da..."
a = re.search(r"\by\w+", txt)
print(a.string)
Bu sabah yağmur var İstanbul'da...
group() Metodu
Belirtilen desen ile eşleşen değeri döndürür.
txt = "Bu sabah yağmur var İstanbul'da..."
a = re.search(r"\by\w+", txt)
print(a.group())
yağmur
x = re.match("python", cumle)
ifadesi ile eşleştirme komutunu bir değişkene (x'e) atadık. Hatırlarsanız, bu fonksiyonu komut satırına yazdığımızda bir eşleşme nesnesi elde ediyorduk. İşte burada değişkene atadığımız şey aslında bu eşleşme nesnesinin kendisi oluyor. Bu durumu şu şekilde teyit edebilirsiniz:
cumle = "python güçlü bir programlama dilidir."
x = re.match("python", cumle)
type(x)
re.Match
Gördüğünüz gibi x bir eşleştirme (match) nesnesidir.
group()
metodu, düzenli ifadelerin değil, eşleşme nesnelerinin bir metodudur.
Bu metodu kullandığımızda direkt aranan değer ekrana yazdırılacaktır. Bu aşamada, metodun gereksiz ya da saçma olduğunu düşünüyor olabilirsiniz. Eşleşme Nesnelerinin Metotları başlığı altında group()
metodunun ne işe yaradığını daha detaylı inceleyeceğiz.
x.group()
'python'
match()
metodu ile, cumle değişkeni içerisinde Java kelimesini arayıp, sonucu inceleyelim.
cumle = "python güçlü bir programlama dilidir."
print(re.match("Java", cumle))
None
Python, match() metodu yardımıyla aradığımız şeyi eşleştirdiği (bulduğu) zaman bir eşleşme nesnesi (match object) döndürüyor. Eğer eşleşme yoksa, o zaman da None değerini döndürüyor.
cumle = "python güçlü bir programlama dilidir."
print(re.match("güçlü", cumle))
None
cumle değişkeninde güçlü ifadesi geçtiği halde match()
metodu bize bir eşleşme nesnesi döndürmedi. Peki ama neden?
Aslında bu gayet normal. Çünkü match()
metodu bir karakter dizisinin sadece en başına bakar.
Yani "python güçlü bir programlama dilidir." ifadesini tutan cumle değişkenine re.match(“güçlü”, cumle)
gibi bir fonksiyon uyguladığımızda, match()
metodu cumle değişkeninin yalnızca en başına bakacak ve cumle değişkeninin en başında güçlü yerine python ifadesini gördüğü için, bize olumsuz yanıt verecektir.
Aslında match()
metodunun yaptığı bu işi, karakter dizilerinin split()
metodu yardımıyla da yapabiliriz:
search() Metodu
search()
metodu ile match()
metodu arasında çok önemli bir fark vardır. match()
metodu bir karakter dizisinin en başına bakıp bir eşleştirme işlemi yaparken, search()
metodu karakter dizisinin genelinde bir arama işlemi yapar. Yani biri eşleştirir, öbürü arar.
search()
metodu, eşleşmenin gerçekleştiği ilk değeri döndürür. Aranan değer ya da desen, karakter dizisi içerisinde birden fazla geçse bile, ilk eşleşmede işlem sonlanır.
güçlü ifadesini search()
metodu yardımı ile, cumle değişkeni içerisinde arayalım.
cumle = "python güçlü bir programlama dilidir."
re.search("güçlü", cumle)
<re.Match object; span=(7, 12), match='güçlü'>
Gördüğünüz gibi, search()
metodu güçlü kelimesini buldu. Çünkü search()
metodu, match()
metodunun aksine, bir karakter dizisinin sadece baş tarafına bakmakla yetinmiyor, karakter dizisinin geneli üzerinde bir arama işlemi gerçekleştiriyor.
Tıpkı match()
metodunda olduğu gibi, search()
metodunda da span()
ve group()
metotlarından faydalanarak bulunan şeyin hangi aralıkta olduğunu ve bu şeyin ne olduğunu görüntüleyebiliriz:
kardiz = "Python güçlü bir dildir"
nesne = re.search("güçlü", kardiz)
nesne.span()
(7, 12)
nesne.group()
'güçlü'
Start() Medodu
Arama deseni ile eşleşen ifadenin, karakter dizisinin kaçıncı karakterinde başladığını görmek için start()
metodunu kullanabiliriz ;
nesne.start()
7
End() Medodu
Arama deseni ile eşleşen ifadenin, karakter dizisinin kaçıncı karakterinde bittiğini görmek için end()
metodunu kullanabiliriz ;
nesne.end()
12
Şimdiye kadar hep karakter dizileri üzerinde çalıştık. İsterseniz biraz da listeler üzerinde örnekler verelim.
Şöyle bir listemiz olsun:
liste = ["elma", "armut", "kebap"]
re.search("kebap", liste)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_3704/1944706800.py in <module>
1 liste = ["elma", "armut", "kebap"]
----> 2 re.search("kebap", liste)
/usr/lib/python3.10/re.py in search(pattern, string, flags)
198 """Scan through string looking for a match to the pattern, returning
199 a Match object, or None if no match was found."""
--> 200 return _compile(pattern, flags).search(string)
201
202 def sub(pattern, repl, string, count=0, flags=0):
TypeError: expected string or bytes-like object
Ne oldu? Hata aldınız, değil mi? Bu normal. Çünkü düzenli ifadeler karakter dizileri üzerinde işler. Bunlar doğrudan listeler üzerinde işlem yapamaz. O yüzden bizim Python’a biraz yardımcı olmamız gerekiyor:
liste = ["elma", "armut", "kebap"]
for i in liste:
nesne = re.search("kebap", i)
if nesne:
print(nesne.group())
kebap
findall() Metodu
Bir metin içinde geçen belirli kelimelerin tümünü bulmak istiyorsak findall()
metodunu kullanmalıyız.
metin = """Guido Van Rossum Python'ı geliştirmeye 1990 yılında başlamış... Yani aslında Python için nispeten yeni
bir dil denebilir. Ancak Python'un çok uzun bir geçmişi olmasa da, bu dil öteki dillere kıyasla kolay olması, hızlı
olması, ayrı bir derleyici programa ihtiyaç duymaması ve bunun gibi pek çok nedenden ötürü çoğu kimsenin gözdesi
haline gelmiştir. Ayrıca Google'ın da Python'a özel bir önem ve değer verdiğini, çok iyi derecede Python bilenlere
iş olanağı sunduğunu da hemen söyleyelim. Mesela bundan kısa bir süre önce Python'ın yaratıcısı Guido Van Rossum
Google'de işe başladı..."""
Yukarıdaki metin değişkeni içinde geçen bütün Python kelimelerini bulmak istersek aşağıdaki kodu çalıştırmalıyız;
print(re.findall("Python", metin))
['Python', 'Python', 'Python', 'Python', 'Python', 'Python']
Gördüğünüz gibi, metinde geçen bütün “Python” kelimelerini bir çırpıda liste olarak aldık.
finditer() Metodu
Belirtilen desenlerin karakter dizileri içerisindeki konumunu bulmak için finditer()
metodunu kullanabiliriz. finditer()
metodu liste döndürmez, iterable bir nesne döndürür.
Örneğin, yukarıda belirtilen metin değişkeni içerisinde Python ifadesinin (deseninin) hangi konumda olduğunu (aranan desenin başlangıç ve bitiş değerlerini) bulmaya çalışalım.
for i in re.finditer("Python", metin):
print(i.span())
(17, 23)
(77, 83)
(128, 134)
(370, 376)
(430, 436)
(522, 528)
desen = r"\d{4}"
eslesenler = re.finditer(desen, metin)
for eslesen in eslesenler:
print(eslesen.group())
1990
split() Metodu
split()
metodu veriyi eşleşmelerin olduğu noktalardan böler ve liste haline getirir. Örneğin aşağıdaki kod çalıştırılırsa cümle boşluk (\s
) karakterlerinden bölünür (yani kelimelere ayrılır):
desen = r"Python"
bol = re.split(desen, metin)
print(bol)
['Guido Van Rossum ', "'ı geliştirmeye 1990 yılında başlamış... Yani aslında ", ' için nispeten yeni\nbir dil denebilir. Ancak ', "'un çok uzun bir geçmişi olmasa da, bu dil öteki dillere kıyasla kolay olması, hızlı\nolması, ayrı bir derleyici programa ihtiyaç duymaması ve bunun gibi pek çok nedenden ötürü çoğu kimsenin gözdesi\nhaline gelmiştir. Ayrıca Google'ın da ", "'a özel bir önem ve değer verdiğini, çok iyi derecede ", ' bilenlere\niş olanağı sunduğunu da hemen söyleyelim. Mesela bundan kısa bir süre önce ', "'ın yaratıcısı Guido Van Rossum \nGoogle'de işe başladı..."]
Gördüğünüz gibi, metin, Python ibaresi gördüğün yerden itibaren parçalara ayrılarak liste oluşturdu. Python ibaresi listeye dahil edilmedi.
cumle = "python güçlü bir programlama dilidir."
x = re.split("\s", cumle)
print(x)
['python', 'güçlü', 'bir', 'programlama', 'dilidir.']
cumle.split()[0] == "python"
True
cumle.startswith("python")
True
cumle.split()[0] == "güçlü"
False
Bölünme sayısı split()
fonksiyonunun 3. parametresinde (maxsplit) belirtilebilir. Aşağıdaki örnekte veri en çok 2 boşluk kadar (3 parça) bölünecektir:
cumle = "python güçlü bir programlama dilidir."
x = re.split("\s", cumle, 2)
print(x)
['python', 'güçlü', 'bir programlama dilidir.']
Aynı işi sadece startswith()
metodunu kullanarak dahi yapabiliriz:
Eğer düzenli ifadelerden tek beklentiniz bir karakter dizisinin en başındaki veriyle eşleştirme işlemi yapmaksa, split()
veya startswith()
metotlarını kullanmak daha mantıklıdır. Çünkü split()
ve startswith()
metotları match()
metodundan çok daha hızlı çalışacaktır.
sub() Metodu
sub()
metodu, eşleşmeleri verilen ifadelerle değiştirir. Substitute kelimesi, yerine koy, değiştir anlamına gelir. sub()
metodunun adın bu kelimeden gelri. Pek çok uygulamadan bildiğimiz Bul ve Değiştir fonksiyonudur aslında.Bu Metod, diğer bölümde detaylı olarak anlatılıyor.
sub()
metodunun;
- ilk argümanı değiştirilecek değeri,
- ikinci argümanı yerine konulacak değeri,
- üçüncü argümanı hangi yapı/ dizgi/ paragraf içinde değişimin yapılacağını,
- dördüncü argümanı kaç eşleşmede değişiklik yapılacağını,
tanımlar.
Aşağıdaki örnekte kavun kelimesini bulacak, ardından karpuz olarak değiştirecektir:
txt = "Bugün kavun yedim."
x = re.sub("kavun", "karpuz", txt)
print(x)
Bugün karpuz yedim.
yukarıdaki açıklamada sub()
metodu ile kaç eşleşmede değişiklik yapılacağı, metodun 4. parametresinde (count) belirtilebileceğinden bahsetmiştik. Basit bir örnek yapalım;
txt = "Yaz Yaz Yaz Bir Kenara Yaz"
x = re.sub("Yaz", "Çiz", txt, 2)
print(x)
Çiz Çiz Yaz Bir Kenara Yaz