Ders 16 / 16
Düzenli İfadeler
Örüntüdeki yedi açılış parantezinin ikisi yakalamaz ve geriye beş numaralı grup kalır; numara açılış parantezinin sırasına göre verilir, eşleşmeme bir istisna değil None'dır ve eşleşme nesnesi her grubun metnini, aralığını ve adını taşır.
İçindekiler
Önceki ders bu kursun ölçtüğü son sözdizim biçimini geride bıraktı. Şimdiye kadar her ölçüm
dilin kendi protokollerini çağıran bir deyim ya da ifadeydi: for, if, +, with.
Bu dersin konusu ise dilin sözdizimi değildir. Düzenli ifade (regular expression) standart kitaplığın sunduğu ayrı bir küçük dildir: örüntü bir dizgide yazılır, ayrı bir kural kümesine göre yorumlanır ve sonuç bir nesne olarak geri döner. Örüntü eşleme algoritmalarının kendisi — kaba kuvvet arama, karma tabanlı arama, önek tablolu arama — Algoritmalar kursunun metin algoritmaları konusunda kuruldu ve burada tekrarlanmaz. Ölçülecek olan şey başka: eşleşme nesnesinin ne taşıdığı ve örüntüdeki parantezlerin hangi sırayla numaralandığı.
Örüntüde Parantezin İki İşi
Bir örüntüde parantezin iki ayrı işi vardır ve ikisi aynı yazımla görünür. Birincisi gruplama: içindeki parçayı tek bir birim yapar ki tekrar ya da seçim işleci ona uygulanabilsin. İkincisi yakalama: eşleşen metni ayrıca saklar ve numaralar.
Sıradan parantez her iki işi birden yapar. (?: ile açılan parantez yalnız gruplar,
yakalamaz — numara almaz. (?P<ad> ile açılan parantez hem yakalar hem numarasının yanına
bir ad koyar.
Numaralama kuralı tek cümledir: numara, açılış parantezinin soldan sağa sırasına göre verilir. İç içe parantezlerde bile dıştaki önce numaralanır, çünkü onun açılışı daha soldadır. Kapanış sırası hiç bakılmaz.
Grupların Numaralanması
Ölçüm bir ölçüm kaydı satırını çözen tek bir örüntü kurar: istasyon adı, yıl, isteğe bağlı ay, değer ve birim. Örüntü hem adlı hem yakalamayan parantez içerir.
- HD43 — Parantez sayıları örüntü dizgisinin kendisinden sayılır; numaralanan grup sayısı ise derlenmiş örüntünün kendi alanından okunur. İkisi ayrı kaynaktır ve birbirini doğrular.
- HD44 — Kâhin örüntüyü yazan kurgudur: hangi parçanın hangi gruba düşmesi gerektiği önceden bilinir.
- HD45 — Aralıklar eşleşme nesnesinden okunur, elle sayılmaz; karakter aralığıdır, bayt değil.
- HD46 — İsteğe bağlı grup eşleşmediğinde
Nonedöner; bu bir hata değil, grubun katılmadığının bildirimidir. - HD47 — İç içe örüntü ölçümü yalnız numaralamayı sınamak için ayrı tutulur; başka hiçbir şey ölçmez.
"""Yakalama gruplari: parantezlerin numaralanmasi ve eslesme nesnesinin tasidigi.""" import re ORUNTU = r"(?P<istasyon>[a-z]+)-(\d{4})(?:-(\d{2}))?:\s*(-?\d+(?:[.,]\d+)?)\s*(°C|%)" DERLI = re.compile(ORUNTU) print(f"örüntüdeki açılış parantezi {ORUNTU.count('(')} | " f"yakalamayan parantez {ORUNTU.count('(?:')} | " f"adlı grup {ORUNTU.count('(?P<')} | numaralanan grup {DERLI.groups}") print("ad -> numara:", DERLI.groupindex) SATIRLAR = ("kuzey-2031: 12,5 °C", "yamac-2031-07: -3.5 °C", "ozet-2031: 88 %", "bozuk kayit") print() print(f"{'satır':<26s}{'eşleşti':<9s}{'aralık':<10s}{'son grup':>9s} gruplar") for s in SATIRLAR: e = DERLI.search(s) if e is None: print(f" {s:<24s}{'hayır':<9s}{'-':<10s}{'-':>9s} -") else: print(f" {s:<24s}{'evet':<9s}{str(e.span()):<10s}" f"{e.lastindex:>9d} {e.groups()}") print() e = DERLI.search(SATIRLAR[1]) print("ikinci satırın eşleşme nesnesi:") print(f" group(0) (tüm eşleşme): {e.group(0)!r}") for i in range(1, DERLI.groups + 1): print(f" group({i}): {e.group(i)!r} aralık {e.span(i)}") print(f" adla erişim: {e.group('istasyon')!r} | " f"adlı grup sözlüğü: {e.groupdict()}") print() IC_ICE = re.compile(r"((\d{4})-(\d{2}))") i = IC_ICE.search("yamac-2031-07: -3.5 °C") print(f"iç içe örüntüde numaralanan grup {IC_ICE.groups}; " f"numara açılış parantezinin sırasına göre verilir:") for n in range(1, IC_ICE.groups + 1): print(f" group({n}) = {i.group(n)!r}")
örüntüdeki açılış parantezi 7 | yakalamayan parantez 2 | adlı grup 1 | numaralanan grup 5
ad -> numara: {'istasyon': 1}
satır eşleşti aralık son grup gruplar
kuzey-2031: 12,5 °C evet (0, 19) 5 ('kuzey', '2031', None, '12,5', '°C')
yamac-2031-07: -3.5 °C evet (0, 22) 5 ('yamac', '2031', '07', '-3.5', '°C')
ozet-2031: 88 % evet (0, 15) 5 ('ozet', '2031', None, '88', '%')
bozuk kayit hayır - - -
ikinci satırın eşleşme nesnesi:
group(0) (tüm eşleşme): 'yamac-2031-07: -3.5 °C'
group(1): 'yamac' aralık (0, 5)
group(2): '2031' aralık (6, 10)
group(3): '07' aralık (11, 13)
group(4): '-3.5' aralık (15, 19)
group(5): '°C' aralık (20, 22)
adla erişim: 'yamac' | adlı grup sözlüğü: {'istasyon': 'yamac'}
iç içe örüntüde numaralanan grup 3; numara açılış parantezinin sırasına göre verilir:
group(1) = '2031-07'
group(2) = '2031'
group(3) = '07'
Eşleşme Nesnesinin Taşıdığı
İlk satır aritmetiği veriyor: örüntüde 7 açılış parantezi var, 2’si yakalamayan, geriye
5 numaralanan grup kalıyor. Sayının biri örüntü dizgisinden, öbürü derlenmiş örüntüden
geldi ve tuttular. Adlı grubun numarası kaybolmuyor: istasyon adı 1 numaraya karşılık
geliyor. Ad bir takma addır, numaranın yerine geçmez.
İkinci tablo dört satırı çözüyor. Üçünde eşleşme var, birinde yok. Eşleşenlerin gruplar
sütunu beşerli demet veriyor ve isteğe bağlı ay grubu, ay yazılmamış satırlarda None
dönüyor. None burada bir hata değildir: grubun katılmadığı anlamına gelir ve boş
dizgiden ayrılır — boş dizgi “eşleşti ama içi boştu” demek olurdu.
Üçüncü blok eşleşme nesnesinin taşıdığı bilginin tamamını açıyor. group(0) tüm eşleşmedir
ve bir grup değildir; numaralar ondan sonra başlar. Her grup hem metnini hem aralığını
veriyor: 2031 kaydın 6–10 karakterleri arasında duruyor. Bu aralık, eşleşen metni
kaynağın içinde yerinden çıkarmadan işaret etmeye yarar.
Son blok numaralamayı kesinleştiriyor. İç içe örüntüde 3 grup var ve group(1) dıştaki
bütünü, group(2) yılı, group(3) ayı veriyor. Dıştaki parantez sonra kapanmasına rağmen
önce numaralanıyor, çünkü önce açılıyor.
Aramanın Biçimleri
İkinci ölçüm aynı örüntüyü ayrı arama biçimleriyle çalıştırır ve eşleşmemenin ne ürettiğini sınar.
- HD48 — Üç arama biçimi aynı derlenmiş örüntüyle ve aynı satırlarla denenir; tek değişen çağrılan yöntemdir.
- HD49 — Çoklu arama sayıları listenin uzunluğundan okunur; satır sayısı metnin kendi satırlarından sayılır.
- HD50 — Eşleşmeme durumunda dönen değer doğrudan basılır; sonucun istisna olmadığı koşumla gösterilir.
- HD51 — Konum ölçümü aynı içeriği bir kez dizgi, bir kez bayt dizisi olarak arar; fark yalnız içeriğin türünden gelir.
"""Eslesme aramanin bicimleri, eslesmeme durumu ve dizgi ile bayt uzerinde konum.""" import re ORUNTU = r"(?P<istasyon>[a-z]+)-(\d{4})(?:-(\d{2}))?:\s*(-?\d+(?:[.,]\d+)?)\s*(°C|%)" DERLI = re.compile(ORUNTU) DENEMELER = ("kuzey-2031: 12,5 °C", " kuzey-2031: 12,5 °C", "kuzey-2031: 12,5 °C ek", "bozuk kayit") print(f"{'satır':<28s}{'search':<9s}{'match':<8s}fullmatch") for s in DENEMELER: d = [("evet" if a(s) else "hayır") for a in (DERLI.search, DERLI.match, DERLI.fullmatch)] print(f" {repr(s):<26s}{d[0]:<9s}{d[1]:<8s}{d[2]}") KAYITLAR = ("kuzey-2031: 12,5 °C\n" "yamac-2031-07: -3.5 °C\n" "ozet-2031: 88 %\n" "bozuk kayit\n" "guney-2032: 41 °C") print() bulunan = DERLI.findall(KAYITLAR) yinelenen = list(DERLI.finditer(KAYITLAR)) print(f"metin satırı {len(KAYITLAR.splitlines())} | findall {len(bulunan)} | " f"finditer {len(yinelenen)} | findall öğesinin uzunluğu " f"{len(bulunan[0])} | grup sayısı {DERLI.groups}") print("finditer aralıkları:", [e.span() for e in yinelenen]) print() yok = DERLI.search("bozuk kayit") print("eşleşmeyen aramanın sonucu:", yok, "| istisna fırlattı mı: hayır") try: yok.group(0) except AttributeError as e: print(f"sonuç üzerinde group çağrısı: {type(e).__name__} — " f"birinci dersin sınıflamasında program kusuru") print() print("değiştirme, grupları geri kullanır:") print(" ", DERLI.sub(r"\g<istasyon>=\4\5", KAYITLAR).replace("\n", " | ")) print() METIN = "kuzey yamaç: 12,5 °C" HAM = METIN.encode("utf-8") print(f"karakter {len(METIN)} | bayt {len(HAM)}") print(f"dizgi üzerinde 'C' konumu {re.search(r'C', METIN).start()} | " f"bayt dizisi üzerinde {re.search(rb'C', HAM).start()}")
satır search match fullmatch 'kuzey-2031: 12,5 °C' evet evet evet ' kuzey-2031: 12,5 °C' evet hayır hayır 'kuzey-2031: 12,5 °C ek' evet evet hayır 'bozuk kayit' hayır hayır hayır metin satırı 5 | findall 4 | finditer 4 | findall öğesinin uzunluğu 5 | grup sayısı 5 finditer aralıkları: [(0, 19), (20, 42), (43, 58), (71, 88)] eşleşmeyen aramanın sonucu: None | istisna fırlattı mı: hayır sonuç üzerinde group çağrısı: AttributeError — birinci dersin sınıflamasında program kusuru değiştirme, grupları geri kullanır: kuzey=12,5°C | yamac=-3.5°C | ozet=88% | bozuk kayit | guney=41°C karakter 20 | bayt 22 dizgi üzerinde 'C' konumu 19 | bayt dizisi üzerinde 21
Sayıların Okunması
İlk tablo üç arama biçimini ayırıyor. search örüntüyü her yerde arar ve dört satırın
üçünde buluyor. match yalnız baştan dener: önünde boşluk olan satırda başarısız oluyor.
fullmatch satırın tamamının eşleşmesini ister: sonunda fazladan metin olan satırda
başarısız oluyor. Aynı örüntü, aynı satır, üç ayrı yanıt — hangi soruyu sorduğunuza bağlı.
İkinci satır çoklu aramayı sayıyor: 5 satırlık metinde 4 eşleşme var ve findall ile
finditer aynı sayıyı veriyor. Fark taşıdıkları şeydedir. findall her eşleşme için 5
uzunluğunda bir demet veriyor — yani yalnız grupların metnini; aralık ve ad bilgisi kayboluyor.
finditer ise eşleşme nesnelerinin kendisini veriyor ve aralıklar okunabiliyor. Grup varken
findall bir sadeleştirmedir ve sadeleştirdiği şey, önceki bölümde ölçülen bilginin tamamıdır.
Üçüncü blok eşleşmemenin ne olduğunu söylüyor: sonuç None ve istisna yok. Düzenli
ifade eşleşmemeyi bir hata saymaz, çünkü eşleşmeme beklenen bir sonuçtur. İstisna ancak
None üzerinde group çağrılırsa doğar ve o zaman gelen sınıf AttributeError olur —
birinci dersin sınıflamasında bir program kusuru, yani except ile ele alınacak değil,
sınamayla önlenecek bir şey. Arama sonucu kullanılmadan önce sınanır.
Değiştirme satırı grupların ikinci işini gösteriyor: yakalanan parçalar yerine konan metinde numarayla ya da adla geri kullanılabiliyor. Eşleşmeyen satır olduğu gibi kalıyor.
Son iki satır bu kursun üçüncü dersine bağlanıyor. Aynı içerik dizgi olarak 20 karakter, bayt dizisi olarak 22 bayt; aranan harfin konumu birinde 19, ötekinde 21. Düzenli ifade dizgide karakter, bayt dizisinde bayt sayar. Örüntü ile arandığı şeyin türü aynı olmalıdır ve dönen aralıklar o türün birimindedir.
Özet
- Parantezin iki işi vardır: gruplama ve yakalama;
(?:yalnız gruplar,(?P<ad>hem yakalar hem ad verir — örüntüdeki 7 açılış parantezinin 2’si yakalamaz, geriye 5 grup kalır. - Numara açılış parantezinin sırasına göre verilir; iç içe örüntüde dıştaki grup önce
numaralanır ve
group(0)bir grup değil, tüm eşleşmedir. - Eşleşme nesnesi her grubun metnini, aralığını ve varsa adını taşır; katılmayan isteğe
bağlı grup
Nonedöner ve bu boş dizgiden ayrı bir bildirimdir. search,matchvefullmatchaynı satırda ayrı yanıt verir;findall5 öğelik demetler döndürüp aralık bilgisini düşürür,finditereşleşme nesnesini korur.- Eşleşmeme bir istisna değil
None’dır; istisna ancak sınanmadangroupçağrılırsa doğar veAttributeErrorolarak gelir.
Kurs Kapanışı
Bu kurs tek bir iddiayı on altı derste ölçtü: Python’da sözdizim bir kısaltmadır. Yazılan her biçim, adı belli bir özel yöntemi ya da adı belli bir kuralı çağırır; bir dersin sayısı, anlattığı sözdizimin hangi protokolü kaç kez çağırdığıdır.
| ders | anlatılan sözdizim | çağrılan protokol |
|---|---|---|
| Python’un Yürütme Modeli | on bir sözdizim biçimi | 12 özel yöntem, toplam 18 çağrı |
| Sözdizim ve Girinti | girinti, blok, deyim ve ifade | özel yöntem yok: girinti birimleri ve ağaç |
| Değişkenler ve Veri Tipleri | ad bağlama, x += ek |
__add__ ile __iadd__ ayrımı, nesne kimliği |
| İşleçler | n + m, n += m, x in n |
__add__, __iadd__, __contains__ yoksa __iter__ |
| Dizgilerle Çalışma | dizgi yöntemleri ve biçimlendirme yazımları | __format__, __str__, __repr__; değiştirilemezlik yeni nesne kurdurur |
| Tip Dönüşümü | ||
| Koşullar | if n |
__bool__, yoksa __len__, o da yoksa doğru |
| Döngüler | for x in n |
__iter__ ve __next__; bitişi StopIteration verir |
| Fonksiyon Tanımı | def, varsayılan değer, argüman biçimleri |
varsayılan tanım anında bir kez değerlendirilir |
| Kapsam Kuralları | ad okuma ve atama | yerel, kapsayan, genel, yerleşik dört düzeyli arama |
| Yerleşik Fonksiyonlar | ||
| İstisnalar | try, except, raise, finally |
istisna hiyerarşisi; yakalama sınıf soyağacına bakar |
| Özel İstisnalar | class ...(Exception) |
sınıfın hiyerarşideki yeri yakalama davranışını belirler |
| Dosya İşlemleri | open metin kipi ve ikili kip |
kodlama çözücüsü ve satır sonu çevirisi |
| Bağlam Yöneticileri | with n |
__enter__, __exit__ ve dönüş değerinin yutma kararı |
| Düzenli İfadeler | örüntü, grup, değiştirme | eşleşme nesnesi ve yakalama grubu numaralaması |
Tablonun boş satırları, bu ders yazıldığı sırada diskte bulunmayan derslerdir; onlar için satır uydurulmadı.
İkinci sütunla üçüncü sütun arasındaki mesafe kursun bütün bulgusudur. Aynı görünen iki
yazım ayrı protokol çağırır — n + m ile n += m gibi. Aynı yazım, nesneye göre ayrı
protokol çağırır — if n gibi. Ve bir protokolün başarısız olması da protokolün
parçasıdır: döngüyü StopIteration bitirir, with bloğundaki istisnayı __exit__ görür ve
dönüş değeriyle onu yutup yutmayacağına karar verir.
Buradan çıkan son okuma şudur: bir nesnenin ne yapabildiği, tanımladığı özel yöntemlerle
belirlenir; tipinin adıyla değil. Kurs boyunca ölçülen nesne hiçbir yerleşik koleksiyondan
inmedi; for içine girebilmesinin, in ile sınanabilmesinin, with bloğunda
kullanılabilmesinin tek gerekçesi ilgili yöntemleri tanımlamış olmasıydı.
Bu kurs boyunca protokoller hep kullanıldı. Bir sonraki kurs olan Veri Yapıları ve
Fonksiyonel Araçlar işin öteki yarısını alır: protokolü yazmak. İkisi ayrı şeylerdir.
for yazan biri yineleme protokolünün çağıranıdır; kendi yineleyicisini, kendi üretecini,
kendi sıralama ölçütünü yazan biri ise protokolün karşı tarafını kurar. Listeler, demetler,
sözlükler ve kümeler orada birer veri yapısı olarak değil, birer protokol gerçekleştirimi
olarak ele alınır — ve kendi nesnenizi onların yerine geçirebilmenizin koşulu, bu kursta
sayılan yöntemleri kendinizin tanımlamasıdır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.