---
title: 'Düzenli İfadeler'
source: 'https://academia.sh/tr/kurslar/java-standart-kutuphane/duzenli-ifadeler'
course: 'Standart Kütüphane ve Akışlar'
language: tr
updated: '2026-08-17T18:09:43+00:00'
license: 'CC BY-SA 4.0'
---

# Düzenli İfadeler

Örüntünün derlenmiş bir değer olduğu, tam eşleşme ile aramanın ayrı sözleşmeler taşıdığı ve açgözlü ile isteksiz niceleyicinin aynı girdide ayrı yakalama ürettiği ölçülür. Örüntüyü her çağrıda yeniden derlemenin etkisiz olduğu ve geri izlemeli eşleştirmede adım sayısının girdiyle doğrusal artmadığı gösterilir.

Önceki ders üç zaman tipinin hangi bilgiyi taşıyıp hangisini taşımadığını ölçtü. Bu ders,
kursun ve kursun son konusunun sorusunu bu kez **metin** üzerinde sorar: bir dizginin bir
örüntüye uyup uymadığını sormak. Java'nın düzenli ifade API'si iki parçadan kurulur —
`Pattern`, örüntünün kendisi; `Matcher`, bir örüntüyü belirli bir girdiye uygulayan taraf —
ve bu ayrımın kendisi kursun sorusuna zaten bir ipucu verir: örüntü bir kere kurulup tekrar
tekrar kullanılabilecek bir **değerdir**, tek seferlik bir çağrı değil.

Dört bölüm sırayla ilerliyor: örüntünün bir değer oluşu ve iki eşleştirme yönteminin ayrı
sözleşmesi, iki niceleyici biçiminin aynı girdide ayrı sonucu, kursun tek **etkisiz** satırı
(kural kırılıyor, sonuç değişmiyor, yalnız bedel değişiyor) ve son sınırlayıcı ölçüm —
geri izlemenin adım sayısı girdiyle **doğrusal artmıyor**.

Düzenli ifadelerin sözdizimi Linux müfredatındaki Kabuk Programlama kursunda ve Python
Temelleri kursunda zaten kurulmuştu; o sözdizim burada tekrar anlatılmıyor. Bu dersin payı
Java'nın kendi düzeneğidir: örüntünün **derlenmiş bir nesne** olarak var olması, eşleştirmenin
bu nesne üzerinden ayrı yöntemlerle çağrılması ve bu yöntemlerin çağıranı hangi kararlara
zorladığı.

## Örüntü Derlenmiş Bir Değerdir

- **IO25** — Tek bir `Pattern` nesnesi dört ayrı girdi üzerinde hem `matches` (tam eşleşme)
  hem `find` (arama) ile sınanıyor. Aynı örüntü, aynı girdi, iki ayrı yöntem.

```java
// TamEslesmeVeArama.java — oruntu derlenmis bir degerdir, matches ve find ayri sozlesme tasir
import java.util.regex.*;

public class TamEslesmeVeArama {
    public static void main(String[] args) {
        Pattern oruntu = Pattern.compile("[0-9]+");
        String[] girdiler = { "12345", "kod-12345", "12345-son", "abc" };

        System.out.printf("%-16s%-10s%s%n", "girdi", "matches", "find");
        for (String g : girdiler) {
            Matcher m1 = oruntu.matcher(g);
            boolean tamEslesme = m1.matches();
            Matcher m2 = oruntu.matcher(g);
            boolean arama = m2.find();
            System.out.printf("%-16s%-10s%s%n", g, tamEslesme, arama);
        }
    }
}
```

```
girdi           matches   find
12345           true      true
kod-12345       false     true
12345-son       false     true
abc             false     false
```

`Pattern.compile` çağrısı bir kere yapılıyor ve dönen `oruntu` dört ayrı `Matcher`'a temel
oluyor — örüntünün kendisi bir çağrı değil, bir **değer**. Bu değer üzerinde çağrılan iki
yöntem farklı sorular soruyor. `matches` girdinin **tamamının** örüntüye uyup uymadığını
soruyor; `kod-12345` girdisinde rakamlar var ama başında `kod-` fazlalığı olduğu için tüm
girdi uymuyor, `matches` `false` dönüyor. `find` ise girdinin **bir yerinde** örüntüye uyan
bir alt dizgi arıyor; aynı girdide `12345` alt dizgisi bulunduğu için `find` `true` dönüyor.
Yalnız son satırda ikisi de aynı yanıtı veriyor, çünkü `abc` girdisinde hiçbir rakam yok —
ne tam eşleşme ne arama bir şey bulabiliyor. `Pattern` aynı kalırken sözleşmeyi değiştiren
şey çağrılan yöntemdir, örüntünün kendisi değil.

## Açgözlü ve İsteksiz Niceleyici Ayrı Yakalama Üretir

- **IO26** — Aynı yakalama grubu iki niceleyici biçimiyle yazılıyor: `(.*)` açgözlü,
  `(.*?)` isteksiz. İkisi de aynı girdiye, aynı köşeli parantez çiftlerine uygulanıyor.

```java
// AcgozluVeIsteksiz.java — acgozlu ve isteksiz niceleyici ayni girdide ayri yakalama uretir
import java.util.regex.*;

public class AcgozluVeIsteksiz {
    public static void main(String[] args) {
        String girdi = "[kayit-1][kayit-2]";

        Pattern acgozlu = Pattern.compile("\\[(.*)\\]");
        Pattern isteksiz = Pattern.compile("\\[(.*?)\\]");

        Matcher m1 = acgozlu.matcher(girdi);
        m1.find();
        Matcher m2 = isteksiz.matcher(girdi);
        m2.find();

        System.out.println("girdi: " + girdi);
        System.out.println("acgozlu  [(.*)]   yakalanan: " + m1.group(1));
        System.out.println("isteksiz [(.*?)]  yakalanan: " + m2.group(1));

        int acgozluAdet = 0;
        Matcher m3 = acgozlu.matcher(girdi);
        while (m3.find()) acgozluAdet++;
        int isteksizAdet = 0;
        Matcher m4 = isteksiz.matcher(girdi);
        while (m4.find()) isteksizAdet++;
        System.out.println("acgozlu toplam eslesme sayisi: " + acgozluAdet);
        System.out.println("isteksiz toplam eslesme sayisi: " + isteksizAdet);
    }
}
```

```
girdi: [kayit-1][kayit-2]
acgozlu  [(.*)]   yakalanan: kayit-1][kayit-2
isteksiz [(.*?)]  yakalanan: kayit-1
acgozlu toplam eslesme sayisi: 1
isteksiz toplam eslesme sayisi: 2
```

İki niceleyici de "sıfır ya da daha çok karakter" anlamına gelir; ikisi de **aynı** kümeyi
kabul eder. Aralarındaki fark kabul ettikleri küme değil, o kümeyi **hangi sırayla** dener.
Açgözlü niceleyici önce mümkün olan en uzun diziyi dener ve eşleşme başarısız olursa geri
çekilir; girdinin sonuna kadar giden `.*` ile başlıyor, sonra kapanış parantezi bulunana
kadar geriye doğru bırakıyor. Girdide iki kapanış parantezi olduğu için en son bulduğu
parantez kazanıyor — yakalanan dizgi iki kaydın arasındaki her şeyi, `kayit-1][kayit-2`'yi
kapsıyor. İsteksiz niceleyici tam tersini yapıyor: önce en kısa diziyi, boş dizgiyi dener ve
yalnız gerektiğinde uzatır; ilk kapanış parantezinde durur. Bu farkın somut sonucu eşleşme
sayısında görünüyor: açgözlü örüntü girdinin tamamını **bir** eşleşmede tüketirken, isteksiz
örüntü aynı girdide **iki** ayrı eşleşme buluyor.

## Çağıranın Kuralı: Yeniden Derleme Etkisizdir

- **IO27** — Beş girdi iki yoldan işleniyor: her seferinde yeniden derlenen bir örüntüyle ve
  bir kere derlenip beş kez kullanılan bir örüntüyle. Derleme çağrılarının sayısı ayrıca
  sayılıyor.

```java
// YenidenDerleme.java — oruntuyu her cagrida yeniden derlemek etkisizdir: sonuc ayni, bedel ayri
import java.util.List;
import java.util.regex.*;

public class YenidenDerleme {
    static int derlemeSayaci = 0;

    static Pattern derle(String duzenli) {
        derlemeSayaci++;
        return Pattern.compile(duzenli);
    }

    static boolean herCagridaDerleyerek(String duzenli, String girdi) {
        return derle(duzenli).matcher(girdi).find();
    }

    public static void main(String[] args) {
        List<String> girdiler = List.of("alfa-01", "beta-02", "gamma-03", "delta-04", "epsilon-05");
        String duzenli = "^[a-z]+-[0-9]{2}$";

        derlemeSayaci = 0;
        boolean[] sonuc1 = new boolean[girdiler.size()];
        for (int i = 0; i < girdiler.size(); i++)
            sonuc1[i] = herCagridaDerleyerek(duzenli, girdiler.get(i));
        int herCagridaDerlemeSayisi = derlemeSayaci;

        derlemeSayaci = 0;
        Pattern tekOruntu = derle(duzenli);
        boolean[] sonuc2 = new boolean[girdiler.size()];
        for (int i = 0; i < girdiler.size(); i++)
            sonuc2[i] = tekOruntu.matcher(girdiler.get(i)).find();
        int birKereDerlemeSayisi = derlemeSayaci;

        System.out.println("sonuclar ayni mi: " + java.util.Arrays.equals(sonuc1, sonuc2));
        System.out.println("her cagrida derleyerek - derleme sayisi: " + herCagridaDerlemeSayisi);
        System.out.println("bir kere derleyerek    - derleme sayisi: " + birKereDerlemeSayisi);
    }
}
```

```
sonuclar ayni mi: true
her cagrida derleyerek - derleme sayisi: 5
bir kere derleyerek    - derleme sayisi: 1
```

Bu ölçüm kursun kurduğu üçüncü sınıfı örnekliyor: **etkisiz** kural kırılması. Beş girdi de
iki yoldan birebir aynı sonuca ulaşıyor — hangi girdinin örüntüye uyduğu, hangisinin
uymadığı iki yolda da özdeş. Yeniden derleme çağıranın kuralını kırıyor ("aynı örüntü tekrar
tekrar kullanılacaksa bir kere derlenmeli") ama bu kırılma ne bir istisna fırlatıyor ne
sessizce yanlış bir sonuç üretiyor; **doğruluk hiç bozulmuyor**. Bozulan şey yalnız bedel:
beş girdilik bir döngüde derleme çağrısı bire karşı beş oluyor. `Pattern.compile` küçük bir
örüntü için ucuz görünebilir, ama derleme işi örüntü metnini bir durum makinesine çeviren
gerçek bir çalışmadır ve her çağrıda yeniden yapılır. Kuralın kırılması burada görünmez
kalır, çünkü hiçbir çıktı satırı yanlış değildir — yalnız harcanan iş fazladır.

Kursun ortak tanımı üç sınıf tanımlıyordu: `istisna` (hemen ve adıyla düşer), `sessiz`
(sonuç yanlıştır, kimse şikâyet etmez), `etkisiz` (kural doğruluğa değil bedele ilişkindir).
On beş gözlemin on dördü ilk ikisine düşüyordu; bu ölçüm on beşincisi ve kursun tek
`etkisiz` satırı — en **kolay gözden kaçan** kırılma, çünkü görünür hiçbir izi yoktur;
yalnız aynı işi yapan iki kod arasındaki iş miktarı farkıdır.

## Sınırlayıcı Ölçüm: Geri İzleme Adım Sayısı Doğrusal Artmaz

- **IO28** — Girdi, kendi `charAt` çağrılarını sayan bir dizi ile sarmalanıyor. Aynı örüntü,
  gitgide uzayan ama hiçbir zaman eşleşmeyen bir girdiye uygulanıyor; her denemede toplam
  okuma sayısı kaydediliyor. Ölçü **adım sayısıdır, süre değildir**.

```java
// SayanDizi.java — girdiyi sarmalayip charAt cagrisini sayan CharSequence (yardimci sinif, main yok)
class SayanDizi implements CharSequence {
    private final String temel;
    long okumaSayisi = 0;

    SayanDizi(String temel) { this.temel = temel; }

    @Override public int length() { return temel.length(); }

    @Override public char charAt(int i) {
        okumaSayisi++;
        return temel.charAt(i);
    }

    @Override public CharSequence subSequence(int b, int s) { return temel.subSequence(b, s); }

    @Override public String toString() { return temel; }
}
```

```java
// GeriIzlemeAdimlari.java — geri izlemeli eslestirmede adim sayisi girdiyle dogrusal artmaz
import java.util.Locale;
import java.util.regex.Pattern;

public class GeriIzlemeAdimlari {
    static long adimSay(Pattern p, String girdi) {
        SayanDizi d = new SayanDizi(girdi);
        p.matcher(d).matches();
        return d.okumaSayisi;
    }

    public static void main(String[] args) {
        Pattern oruntu = Pattern.compile("(a+)+b");

        System.out.printf(Locale.ROOT, "%-10s%s%n", "uzunluk", "charAt cagri sayisi");
        long onceki = 0;
        for (int uzunluk = 10; uzunluk <= 25; uzunluk += 5) {
            String girdi = "a".repeat(uzunluk);
            long adim = adimSay(oruntu, girdi);
            System.out.printf(Locale.ROOT, "%-10d%d%n", uzunluk, adim);
            if (onceki > 0) {
                double oran = (double) adim / onceki;
                System.out.printf(Locale.ROOT, "  (bir onceki uzunluga gore artis orani: %.1f)%n", oran);
            }
            onceki = adim;
        }
    }
}
```

```
uzunluk   charAt cagri sayisi
10        100
15        225
  (bir onceki uzunluga gore artis orani: 2.3)
20        400
  (bir onceki uzunluga gore artis orani: 1.8)
25        625
  (bir onceki uzunluga gore artis orani: 1.6)
```

Girdi `a` harflerinden oluşuyor ve `b` hiç içermiyor; örüntü `(a+)+b` ise sonunda mutlaka bir
`b` istiyor. Eşleşme baştan başarısız olacak, ama motor bunu anlayana kadar iç grup `(a+)`'yı
girdiyi **kaç harfte bir bölerek** deneyeceğini araştırıyor — bir harf mi, iki mi, üç mü —
ve her bölünüşte `b` bulunamadığı için bir öncekine geri dönüp başka bir bölünüşü deniyor.
Girdi uzunluğu 10'dan 25'e, yani iki buçuk katına çıktığında, okuma sayısı 100'den 625'e,
altı buçuk katına çıkıyor: 10 uzunlukta 100, 15'te 225, 20'de 400, 25'te 625 — her satır bir
öncekinin **karesiyle orantılı** büyüyor. Girdi doğrusal (sabit adım sabit artış) uzarken
adım sayısı karesel büyüyor; bu, "girdiyle doğrusal artmaz" iddiasının ölçülmüş hâli. Ölçü
gerçek bir kronometre değil, `charAt` çağrısı sayan bir sarmalayıcı — bu yüzden sonuç bu
belgeyi kim, hangi makinede çalıştırırsa çalıştırsın **birebir aynı** çıkıyor.

`(a+)+b` örüntüsünün kendisi geçerli, sıradan bir düzenli ifade; onu **tehlikeli** yapan bir
kaçış ya da özel bir kurgu değil, iç içe geçmiş bir niceleyicinin aynı karakterleri birden
çok yoldan gruplayabilmesidir — bu ders bu geometriyi kurmuyor, yalnız sayıyor.

## Özet

- `Pattern` derlenmiş bir değerdir; `matches` (tam eşleşme) ve `find` (arama) aynı örüntü
  üzerinde ayrı sözleşmeler taşır ve aynı girdide ayrı sonuç verebilir.
- Açgözlü niceleyici (`.*`) önce en uzun diziyi dener ve geriye doğru daralır; isteksiz
  niceleyici (`.*?`) önce en kısa diziyi dener ve gerektikçe uzar; aynı girdide ayrı
  yakalama ve ayrı eşleşme sayısı üretirler.
- Örüntüyü her çağrıda yeniden derlemek kursun tek `etkisiz` kural kırılmasıdır: eşleşme
  sonucu değişmez, yalnız derleme çağrısı sayısı artar.
- Geri izlemeli bir eşleştirmede adım sayısı girdiyle doğrusal artmayabilir; ölçülen örnekte
  girdi uzunluğu iki buçuk katına çıkarken adım sayısı altı buçuk katına, karesel olarak
  çıktı.
- Adım sayısı gerçek bir kronometreyle değil, girdiyi sarmalayıp okuma çağrısını sayan bir
  `CharSequence` ile ölçüldü; sonuç bu yüzden makineden bağımsız ve yeniden üretilebilir.

## Kurs Kapanışı

Kurs tek bir soruyla açıldı: yazılmış bir kütüphaneyi çağırdığın anda karar dilin dışına
çıkar — peki o kütüphanenin sana verdiği sözü kim veriyor? On beş ders bu soruyu on beş ayrı
davranışa sordu ve yanıtı her seferinde en az iki gerçekleştirimi ya da en az iki çağrı
biçimini yan yana koşturarak aldı. Ana iddia doğrulandı: on beş gözlemin yedisi arayüz
garantisiydi, sekizi yalnız seçilen gerçekleştirimin davranışıydı — tek bir sınıfı gözleyerek
çıkarılan bir davranış listesinin çoğunluğu arayüzün sözü değildi. İkinci iddia da doğrulandı:
çağıranın kuralı kırıldığında en sık düşen şey bir istisna değil, sessizce yanlış bir
sonuçtu. Üçüncü iddia sınırıyla birlikte doğrulandı: derleyicinin koruması burada tümüyle
bitmiyor, koruduğu şey değişiyor. Tek soyut yöntem kısıtı ile etkin `final` kuralı hâlâ
derleme zamanında düşüyor; ikisi de bir **tipin şekline** ilişkin sözlerdir. Ama kursun
ölçtüğü çağıran yükümlülüklerinin hiçbiri derleme zamanında görünmedi — hepsi derlendi ve
ayrım çalışma zamanına kaldı. Derleyici tipin şeklini doğruluyor, sözün tutulduğunu değil.

| Ders | Ölçülen davranış | Garantiyi veren | Sınırlayıcı ölçüm |
|---|---|---|---|
| Koleksiyon Çerçevesi | 15 gözlemin 7'si arayüz garantisi, 8'i gerçekleştirim davranışı | arayüz (7) + gerçekleştirim (8) | 4 ögeli kaynakta `PriorityQueue`'nun gezinme sırası çıkış sırasıyla rastlantı eseri örtüşür; 5. öge ayrımı açar |
| Liste, Küme ve Kuyruk | 6 ögelik girdiden liste hiçbir bilgi, küme 3 bilgi, kuyruk yalnız konum erişimini düşürür | arayüz (düşen bilgi sözün bedeli) + çağıran (`remove(1)`) | `LinkedList` iki arayüzü birden konuşur; aynı `remove(1)` iki anlam taşır, kırılma sessizdir |
| Sözlükler | Üç söz (anahtar tekilliği, canlı görünüm, yok anahtarda boş değer) üç gerçekleştirimde aynı; sıra ve boş anahtar ayrışır | arayüz (3 söz) + gerçekleştirim (sıra) + çağıran (`equals`/`hashCode`) | Aynı kusurlu anahtar sıralı sözlükte bulunur; orada kullanılan söz `compareTo`'dur |
| Yineleyiciler ve Eşzamanlı Değişiklik | Tükenmede `NoSuchElementException` üç gerçekleştirimde aynı; gezinirken değişiklikte üç ayrı yanıt | arayüz (tükenme) + gerçekleştirim (değişiklik yanıtı) | `Iterator.remove` kuralı kırmaz; kural "yineleyicinin dışından değiştirme"dir |
| Karşılaştırma ve Sıralama | Doğal sıra tipin kendi sözü, karşılaştırıcı çağıranın verdiği ayrı nesne; kararlılık iki gerçekleştirimde aynı | arayüz (kararlılık) + çağıran (karşılaştırıcı tutarlılığı) | Uyumsuz karşılaştırıcı listede hiç öge düşürmez; kusuru üreten onu tekillik kararı olarak kullanan kaptır |
| Fonksiyonel Arayüzler | Tek soyut yöntem derleme zamanı sözüdür; davranış sözü hiçbir yerde denetlenmez | arayüz (tipin şekli); davranış sözü kimsenin | İşaret garantiyi eklemez, ihlali erken yakalar; işaretsiz arayüz de fonksiyoneldir |
| Lambda İfadeleri | Yerel değişken değeriyle yakalanır, etkin `final` derleme zamanında zorunlu; alanda kısıt yok | çağıran (hangi biçimi seçtiği) | Kısıt değişkeni dondurur, gösterdiği nesneyi değil; yakalanan liste değişirse lambda yeni içeriği görür |
| Akış API'si | On ölçümün 3'ü kaynaktan bağımsız garanti, 5'i kaynağın sırasına bağlı, 2'si çağıranın kuralı | arayüz (3) + kaynak (5) + çağıran (2) | "Veri akışı sırayı korur" cümlesi kaynağın sözüdür, veri akışının değil |
| Toplayıcılar | Dört parça (üretici, birikeç, birleştirici, bitirici); sonuç kabının tipi arayüzün sözü değildir | gerçekleştirim (kap tipi) + çağıran (yinelenen anahtar) | Çağıran kap üreticisini verdiğinde belirsizlik kapanır; bırakılmış söz geri alınabilir |
| `Optional` | Denetimsiz bırakılan çağrı sayısı iki imzada da eşit (4/4); kazanç imzada görünürlüktür | çağıran (denetim çalışma zamanında kalır) | Alanda ve parametrede kazanç kaybolur; `of(null)` hemen düşer, `ofNullable(null)` düşmez |
| Akış Tabanlı Girdi/Çıktı | `write(int)`in düşük sekiz bit ve `read()`in 0–255 artı `-1` sözü iki gerçekleştirimde aynı; tamponlama yalnız çağrı sayısını değiştirir | arayüz (bayt sözü) + çağıran (boşaltma) | Boşaltılmamış tampon veriyi eksik bırakır; boşaltma kararı çağıranındır |
| Dosya Sistemi API'si | Yol cebiri iki sağlayıcıda da aynı; `exists` ve `size` sağlayıcının deposuna sorar | arayüz (yol cebiri) + gerçekleştirim (depo) + çağıran (iki adım) | Varlık denetimi ile açma iki ayrı işlemdir; arada dosya silinirse denetim geçer, açma düşer |
| Dizileştirme | Yapıcı hiç çalışmaz; kurduğu iki değişmezden sıfırı geri okumada kendiliğinden geçerli kalır | çağıran (`readObject` yazılmazsa denetim yok) | Sürüm kimliği sabit tutulup alan adı değişince eski kayıt sessizce varsayılanla geçer |
| Tarih ve Zaman API'si | `Instant.equals` ve `ZonedDateTime.isEqual` "aynı an mı" sorusunu yanıtlar; `LocalDateTime.equals` yanıtlayamaz | tip (taşınan bilgi) + çağıran (saat dilimi vermeden karşılaştırma) | Yaz saati geçişinde çakışan yerel saat iki ayrı ana karşılık gelir; API erken ofseti seçer |
| Düzenli İfadeler | `Pattern` derlenmiş bir değerdir; `matches` ile `find` ayrı sözleşme, açgözlü ile isteksiz ayrı yakalama üretir | arayüz (`Pattern` sözleşmesi) + çağıran (yeniden derleme: tek `etkisiz` satır) | Geri izlemede adım sayısı girdiyle doğrusal artmaz; ölçü adım sayısıdır, süre değildir |

Standart Kütüphane ve Akışlar burada kapanıyor. Sıradaki kurs olan **JVM, Eşzamanlılık ve
Başarım** aynı sorulara üçüncü bir katmanda döner: bir garantinin arkasında artık tek bir
iş parçacığı değil, birden çok iş parçacığının aynı nesneye aynı anda eriştiği bir çalışma
zamanı vardır — ve o zaman "kim garanti veriyor" sorusunun yanıtı zamanlamaya bağlı hâle
gelebilir.
