Öncelikle Yüksek Lisans öğrencim “Mustafa Salih Bahar”a teşekkürler. Bu bölümde Python ortamında düzenli ifade kullanımı göreceğiz.
Düzenli ifadeler tüm modern programlama dillerde bir standart haline gelmiştir. Karmaşık bir metin içerisinde istediğimiz formattaki metinleri yakalayabilmemize imkân tanır. Mesela, bir kaynakta geçen tüm e-posta adreslerini veya içinde rakam bulunan ve gmail uzantılı olan mail adreslerini ayıklamak için kullanabilirsiniz. Düzenli ifadeler olmasaydı ardı arkasına birçok if — else yazmak gerekebilirdi. Bu modül, birkaç saatte yapabileceğiniz bir işlemi saniyeler içerisinde sizin yerinize yapabiliyor.
1. Kurulum
Python ile standart olarak gelen bir kütüphanedir. Kurulum gerektirmez.
1 |
import re |
şeklinde projeniz içerisine aktardıktan sonra kullanmaya başlayabilirsiniz.
2. Fonksiyonlar
2.1. search()
Bu fonksiyon, aranılan bir içeriğin ilgili metin içerisinde olup olmadığını kontrol eder.
1 |
re.search("Mustafa","Merhaba ben Mustafa, senin adın nedir?") |
1 |
<_sre.SRE_Match object; span=(12, 19), match='Mustafa'> |
Metin içerisinde geçen Mustafa kelimesini arattırdık, dikkat edersek span ve match diye alanlar var. Burada match aradığımız değeri, span ise nerede olduğunu gösterir. Aradığımız yerdeki 12. ve 19. harfler arasındaymış o zaman kontrol edelim.
1 2 |
metin = "Merhaba ben Mustafa, senin adın nedir?" metin[12:19] |
1 |
'Mustafa' |
Gördüğümüz gibi tekrar Mustafa değeri döndü. search() fonksiyonu ilgili metnin nerede olduğunu başarıyla bize söyleyebildi.
2.2. start()
Bu fonksiyon aratılan kelimenin, kaynakta nerede geçtiğini döndürür. Yukarıdaki örnekte (12,19) arasında olduğunu biliyoruz. Sadece buradan 12 değerini çekmek istersek kullanabiliriz.
1 2 |
kontrol = re.search("Mustafa","Merhaba ben Mustafa, senin adın nedir?") kontrol.start() |
1 |
12 |
2.3. end()
Üstteki start() fonksiyonunun tersini yapar, yani aratılan kelime hangi aralıkta geçiyorsa onun son değerini döndürür. Kaynakta Mustafa kelimesinin (12,19) arasında geçtiğini biliyoruz. Start fonksiyonu 12 dönmüştü, end() fonksiyonu da 19 değerini dönecektir.
1 2 |
kontrol = re.search("Mustafa","Merhaba ben Mustafa, senin adın nedir?") kontrol.end() |
1 |
19 |
2.4. endpos()
Kaynak içerisindeki karakterlerin toplam sayısını döndürür. \n gibi satır atlatma karakterleri de sayılır.
1 2 3 |
metin = "Merhaba ben Mustafa, senin adın nedir?" kontrol = re.search("Mustafa",metin) kontrol.endpos |
1 |
38 |
1 |
metin[:38] |
1 |
'Merhaba ben Mustafa, senin adın nedir?' |
İlk metin değişkeni içine bir içerik yazdık, sonra içinde Mustafa geçiyor mu diye bakarak sonucu bir kontrol objesine atadık, ardından endpos() ile 38 karakter olduğunu gördük, sonra metin değişkenini 38’ya kadar görüntüledik ve doğruluğunu teyit ettik.
2.5. findall()
Bu fonksiyon ile bir kaynak içerisinde istediğimiz metnin kaç kere geçtiğini inceleyebiliriz.
1 |
print(re.findall("zaman","Tam zamanında geldin, sensiz zaman geçmiyor")) |
1 |
['zaman', 'zaman'] |
Oluşturduğum metin içerisinde zaman kelimesi var mı diye sorguladık. Liste olarak geri döndü. Eğer bunu adet olarak görmek isteseydik len() fonksiyonundan geçirebilirdik.
1 |
print(len(re.findall("zaman","Tam zamanında geldin, sensiz zaman geçmiyor"))) |
1 |
2 |
Bu şekilde 2 kere geçtiğini de görmüş olduk.
3. Metakarakterler
Bu noktaya kadar bir kaynak içerisinde belirli bir metin aradık, ancak bu modülü güçlü yapan şey bu noktadan sonra anlatacağımız metakarakterlerdir. Bu metakarakterler sayesinde, belirlediğimiz bir düzene uyan metinleri arayabiliriz. Mesela salih metnini aramak yerine, salih, selih, fetih, fatih gibi belirli bir şablon içerisine giren metinleri de arayabiliriz. İşte bunu yapmamızı sağlayan, aranılan metne eklediğimiz bazı özel karakterler var, bu karakterleri inceleyelim.
3.1. . Karakteri
Yeni satır karakteri hariç herhangi tek bir karakterin yerini tutar.
1 |
re.findall(".aman","O her zaman, saman altından su yürütürdü.") |
1 |
['zaman', 'saman'] |
Mesela bu örnek içerisinde zaman ve saman kelimelerini buldu. Çünkü herhangi bir karakter ile başlayıp aman ile devam eden bir şablon belirledik.
3.2. * Karakteri
Bir ifadenin bütün tekrarlanmalarını bulur.
1 |
1 |
['@mail', '@gmail', '@ggggggmail'] |
g harfinden sonra * koyduk, yani bu g karakteri 0 kere de geçebilir 100 kere de geçebilir. Bu ayrım sadece kendinden önceki harfi kapsıyor. Bu nedenle @mail, @gmail ve @ggggggmail döndü.
3.3. + Karakteri
Kendinden önce gelen karakterin bir veya daha fazla kullanılmasını arar. Üstte yaptığımız * ile ilgili örneği bu sefer + ile yaparsak;
1 |
1 |
['@gmail', '@ggggggmail'] |
şeklinde @gmail ve @ggggggmail döner. Çünkü g harfinden sonra + koyarak bu harfin 1 veya daha fazla geçmesi gerektiğini söyledik.
3.4. ? Karakteri
Kendinden önce gelen karakterin 0 veya 1 kere tekrar etmesini sorgular.
1 |
1 |
['@mail', '@gmail'] |
Dikkat edilirse @ggggggmail dönmedi çünkü 0 veya 1 kere olmasını sorguladık, 1’den fazla olanlar bu sonuca dahil değildir.
3.5. [] Karakteri
Bu köşeli parantezler arasına yazılan bütün karakterler sorgulanır.
1 |
re.findall("[HB]ilal","Hilal ve Bilal çok iyi arkadaştır.") |
1 |
['Hilal', 'Bilal'] |
Bu şablonda H veya B ile başlayan sonrasında ilal ile devam eden bir şablon belirledik ve Hilal, Bilal sonuçlarını aldık.
1 |
re.findall("s[ai]m[ei]t","Kardeşim samet, markete simit almaya gitti.") |
1 |
['samet', 'simit'] |
Burada ise s ile başlayan a veya i artı m ile devam eden, e veya i artı t ile biten sonuçları aradık. Sonrasında samet ve simit sonuçlarını başarıyla gördük.
Biraz daha işleri karıştıralım. Köşeli parantez kullanırken aralık da belirleme imkânımız vardır.
- [A-Z] A’dan Z’ye tüm büyük harfler.
- [a-z] A’dan Z’ye tüm küçük harfler.
- [0–9] 0’dan 9’a tüm rakamlar.
- [1–4] 1’den 4’e tüm rakamlar.
O zaman madem Hilal ve Bilal büyük harf ile başlıyor, şöyle yazalım.
1 |
re.findall("[A-Z]ilal","Hilal ve Bilal çok iyi arkadaştır.") |
1 |
['Hilal', 'Bilal'] |
A-Z arasındaki büyük bir harfle başlayan ve sonra ilal ile devam edenleri bulduk. Peki küçük harfle arasaydık ne olurdu?
1 |
re.findall("[a-z]ilal","Hilal ve Bilal çok iyi arkadaştır.") |
1 |
[] |
Gördüğümüz gibi hiçbir sonuç dönmedi. Çünkü Hilal ve Bilal büyük harfler ile başlıyor.
3.6. {} Karakteri
Belirli bir sayıda tekrar anlamındadır. Şimdi yukarıda öğrendiklerimiz ile birlikte biraz daha karışık bir örnek yapalım. Kaynağımız Bence saat tamir etmek zor zanaat. olsun. Burada küçük harflerle başlayan aat ile biten her şeyi yakalamak istedik.
- Adım 1: [a-z] ile küçük harf ayrımı yapmamız gerek.
- Adım 2: [a-z]* ile küçük harflerden 1 veya n (sonsuz) kere geçmesini söyledik.
- Adım 3: [a-z]*a ile küçük harf ile başlayıp a ile devam etsin dedik.
- Adım 4: [a-z]a{2} ile a harfinden 2 adet olması gerektiğini belirttik.
- Adım 5: [a-z]a{2}t en son da t ile bitsin dedik.
1 |
re.findall("[a-z]*a{2}t","Bence saat tamir etmek zor zanaat.") |
1 |
['saat', 'zanaat'] |
3.7. ^ Karakteri
İfadenin başlangıcını kontrol eder ve [ ] karakterleri ile birlikte kullanılırsa da hariç anlamına gelir.
1 |
re.findall("^Oku","Oku oğul, sesli oku.") |
1 |
['Oku'] |
1 |
re.findall("^sesli","Oku oğul, sesli oku.") |
1 |
[] |
Kaynağımız Oku ile başladığı için ilk örnek başarıyla ^Oku sayesinde çalıştı. Ancak diğer örnekte ^sesli ayracı bir şey dönmedi, halbuki kaynak içerisinde bu metin var fakat başında değil.
Bu meta karakter sabit bir metin için kullanışlı gelmeyebilir. Çünkü zaten metnin başını gözle de görebiliyorsunuz, fakat bir listeyi for döngüsüne sokarak böyle bir metakarakter kullanılırsa çok faydalı olabilir. Bir örnek yapalım.
1 2 3 4 5 6 |
metin = "Mustafa başkomiser ve yardımcısı Kemalettin, 34XY6699 plakalı arabanın peşinde." liste = metin.split() for i in liste: sonuc = re.findall("^[A-Z]+[a-z]+",i) if sonuc: print(sonuc) |
1 2 |
['Mustafa'] ['Kemalettin'] |
Örnek içerisinde metin değişkenindeki içeriği split() fonksiyonu ile kelime kelime parçalayıp liste içerisine attık. Sonra da bir for döngüsü ile her bir kelimeyi kontrol ederek büyük harfli kelimeleri ayırdık.
Şimdi de plakayı biraz uzun bir yol kullanarak bulalım.
1 2 3 4 |
for i in liste: sonuc = re.findall("[^A-Za-z-][0-9]+[A-Z]+[0-9]+",i) if sonuc: print(sonuc) |
1 |
['34XY6699'] |
[ ] arasındaki A-Za-z ifadesi büyük veya küçük harf anlamını taşıyor. Başına da ^ eklediğimizde büyük veya küçük harf ile başlamasın demiş oluyoruz. Sonra 0–9 ile devam etsin sonra tekrar büyük harf ve sonra tekrar 0–9 ile devam etsin. Sonuç olarak plakayı çektik.
3.8. $ Karakteri
Yukarıda, ^ karakteri ile bir ifadenin başlangıcını kontrol etmiştik, $ metakarakteri ile de ifadenin sonunu yani ne ile bittiğini kontrol edebiliyoruz.
1 2 3 4 5 6 |
metin = "Silikon vadisi, google.com ve apple.com arasındaki rekabeti tartışıyor." liste = metin.split() for i in liste: sonuc = re.search(".com$",i) if sonuc: print(sonuc.string) |
1 2 |
google.com apple.com |
Örnek bir metin oluşturup içerisinde google.com ve apple.com alan adlarını yerleştirdik. Ardından bu metni split() fonksiyonu ile bir liste içerisine aldık. Sonra bir for döngüsü ile her bir kelimenin .com ile bitip bitmediğini, eğer bitiyorsa ekrana yazmasını istedik. Bu şekilde bir metinde geçen .com ile biten kelimeleri çıkarabildik.
Eğer buradaki .com veya .net ile bitenleri bulmak istersek aradaki veya ifadesini karşılamak için | karakterini kullanıyoruz. Örnek;
1 2 3 4 5 6 |
metin = "Silikon vadisi, google.com ve apple.net arasındaki rekabeti tartışıyor." liste = metin.split() for i in liste: sonuc = re.search("(.com|.net)$",i) if sonuc: print(sonuc.string) |
Bu kod parçası da metin içerisinde geçen .com veya .net geçenleri ekrana bastırır.
3.9. | Karakteri
Yukarıdaki örnek içerisinde kullanılmıştı, veya anlamına gelir.
1 |
re.findall("(siyah|beyaz)","Beşiktaş, siyah ve beyaz renkleri ile anılır.") |
1 |
['siyah', 'beyaz'] |
Kaynaktan siyah veya beyaz metinlerini aldık. Arama yaparken birden fazla değeri kaynak içerisinde bulmayı mümkün kılar.
3.10. ( ) Karakteri
Parantez de yazdığımız kalıpları gruplamak için kullanılır. Matematikteki bölme ve çarpma için öncelik belirlerken kullandığımız parantez gibi düşünebiliriz.
3.11. \ Karakteri
Kaçış dizisidir. Buraya kadar birçok karakter gördük, mesela bunlardan biri de $ simgesi. Bir ifadenin sonunu kontrol etmek için kullanıyorduk. Ancak bu bir dolar simgesi ve bazen bir para birimi olarak da kaynak içerisinde bulunabilir, ve biz bunları ayıklamak isteriz. Mesela;
1 |
re.findall("[0-9]+$","Ekran kartı 150$, ses kartı ise 90$") |
1 |
[] |
1 |
re.findall("[0-9]+\$","Ekran kartı 150$, ses kartı ise 90$") |
1 |
['150$', '90$'] |
0–9 ile başlayan sonra $ ile devam eden bir arama yaptığımızda eğer ters slash koymazsak python bunun özel bir anlam ifade ettiğini zannediyor ve sonuç döndürmüyor. Kaçış karakteri kullandığımızda ise başarıyla fiyatları döndürüyor.
4. Meta Sequences
Metakarakter gibi ama özel bir anlam ifade eden yapılar var son olarak onlara bakalım.
4.1. \s ve \S İfadesi
Bu ifade kaynaktaki boşluğu yakalar. Büyük harfle yazılan hali ise boşluk haricindekileri yakalar.
1 |
re.findall("\s","Bil bakalım kaç boşluk var.") |
1 |
[' ', ' ', ' ', ' '] |
1 |
len(re.findall("\s","Bil bakalım kaç boşluk var.")) |
1 |
4 |
Metin içinde kaç tane boşluk olduğunu görmek için \s ifadesini kullandım. Sonra len() fonksiyonu ile adedini ekrana bastırdık.
Boşluk haricindekileri bulmak istersek, onu da \S ile yapıyoruz.
1 |
re.findall("\S+","Bil bakalım kaç boşluk var.") |
1 |
['Bil', 'bakalım', 'kaç', 'boşluk', 'var.'] |
4.2. \d ve \D İfadesi
Bu ifade kaynaktaki sayıyı yakalar. Büyük harfle yazılan hali ise sayı olmayanları yakalar.
1 |
re.findall("\d+","Gelirken 1 kilo yoğurt ve 2 adet ekmek alırsın.") |
1 |
['1', '2'] |
Başarıyla kaynaktaki sayıları yakaladık. Sayı haricindekileri yakalayalım.
1 |
re.findall("\D+","Gelirken 1 kilo yoğurt ve 2 adet ekmek alırsın.") |
1 |
['Gelirken ', ' kilo yoğurt ve ', ' adet ekmek alırsın.'] |
4.3 \w ve \W İfadesi
Bu ifade kaynaktaki karakter, sayı ve alt çizgiyi yakalar. Büyük harfle yazılan hali ise tam tersini yani boşluk, nokta, soru işareti, boşluk gibi karakterleri yakalar.
1 |
1 |
['Mail', 'adresin', 'mustafa', 'salih', 'test', 'com', 'mu'] |
1 |
1 |
[' ', ' ', '%', '@', '.', ' ', '?'] |
\w kullanıldığında % @ ve ? simgeleri gelmezken \W kullanıldığında geliyor.
Kaynaklar
- https://www.sinanerdinc.com/python-re-modulu
- https://docs.python.org/3/contents.html
- https://www.tutorialspoint.com/python/index.htm
- https://www.w3schools.com/python/