Takip et

Java’da Düzenli İfadeler (Regex): Kapsamlı Bir Rehber ve Örnekler

Java’da Düzenli İfadeler (Regex): Kapsamlı Bir Rehber ve Örnekler Yazılım geliştirme süreçlerinde metin işleme, doğrulama ve ayrıştırm

Java’da Düzenli İfadeler (Regex): Kapsamlı Bir Rehber ve Örnekler

Yazılım geliştirme süreçlerinde metin işleme, doğrulama ve ayrıştırma işlemleri sıkça karşımıza çıkar. E-posta adreslerinin geçerliliğini kontrol etmek, kullanıcı girişlerini belirli bir formata göre doğrulamak, log dosyalarından belirli bilgileri ayıklamak veya karmaşık metinleri analiz etmek gibi pek çok senaryoda güçlü bir araca ihtiyaç duyarız. İşte bu noktada Düzenli İfadeler (Regular Expressions – Regex) devreye girer. Regex, metin desenlerini tanımlamak için kullanılan, son derece esnek ve güçlü bir mini dildir. Java, bu yetenekleri java.util.regex paketi aracılığıyla eksiksiz bir şekilde sunar. Bu makalede, Java’da düzenli ifadelerin temellerinden ileri seviye kullanımına, sözdiziminden pratik örneklere kadar her şeyi detaylı bir şekilde inceleyeceğiz.

Giriş: Düzenli İfadeler Nedir ve Neden Önemlidir?

Düzenli ifadeler, metin dizileri içinde belirli karakter kalıplarını (desenlerini) aramak, eşleştirmek ve manipüle etmek için kullanılan bir karakter dizisi biçimidir. Kelimenin tam anlamıyla, metinler üzerinde güçlü bir arama ve değiştirme motoru gibidirler. İlk olarak 1950’lerde matematikçi Stephen Kleene tarafından geliştirilen düzenli ifadeler, bilgisayar bilimlerinde ve özellikle metin işleme alanında vazgeçilmez bir araç haline gelmiştir.

Düzenli İfadelerin Gücü ve Uygulama Alanları

Düzenli ifadelerin gücü, basit bir karakter aramasından, karmaşık desen eşleştirmelerine kadar geniş bir yelpazede kullanılabilmesinden gelir. Bir metin içinde “elma” kelimesini bulmakla “geçerli bir e-posta adresi” desenini bulmak arasındaki farkı düşünün. İkinci senaryo, sadece belirli karakterlerin varlığını değil, aynı zamanda bu karakterlerin belirli bir sırada ve sayıda olmasını da gerektirir. Düzenli ifadeler, bu tür karmaşık desenleri kısa ve öz bir şekilde ifade etmemizi sağlar.

Uygulama alanları oldukça geniştir:

  • Veri Doğrulama: E-posta adresleri, telefon numaraları, kredi kartı numaraları, şifreler, IP adresleri gibi kullanıcı girdilerinin belirli formatlara uygunluğunu kontrol etmek.
  • Metin Ayrıştırma (Parsing): Log dosyalarından, yapılandırılmamış metinlerden veya HTML/XML belgelerinden belirli bilgileri (tarihler, URL’ler, sayılar vb.) çekmek.
  • Arama ve Değiştirme: Metin editörlerinde, IDE’lerde veya programatik olarak metin içindeki belirli desenleri bulup başka bir metinle değiştirmek.
  • Veri Temizleme: İstenmeyen karakterleri veya formatları metinlerden kaldırmak.
  • Dil İşleme: Doğal dil işleme (NLP) uygulamalarında tokenizasyon, kök bulma gibi ön işlemleri yapmak.

Java’da Düzenli İfadeler: java.util.regex Paketi

Java, düzenli ifade yeteneklerini java.util.regex paketi aracılığıyla sunar. Bu paket, iki ana sınıf etrafında döner: Pattern ve Matcher. Bu sınıflar, düzenli ifade desenlerini derlemek ve bu desenleri hedef metinler üzerinde uygulamak için kullanılır.

Pattern Sınıfı: Deseni Derleme

Pattern sınıfı, bir düzenli ifadeyi derlenmiş bir gösterimine dönüştürmek için kullanılır. Bir düzenli ifade deseni, Java’da bir String olarak ifade edilir. Ancak bu String doğrudan metin üzerinde arama yapamaz. Pattern.compile() metodu kullanılarak derlenmesi gerekir. Derlenmiş bir Pattern nesnesi, birden fazla eşleştirme işlemi için tekrar tekrar kullanılabilir ve bu, performansı artırır, çünkü desenin her seferinde yeniden ayrıştırılmasına gerek kalmaz.


import java.util.regex.Pattern;

public class RegexBaslangic {
    public static void main(String[] args) {
        // Bir düzenli ifade deseni tanımlıyoruz
        String regex = "java";

        // Deseni Pattern nesnesine derliyoruz
        Pattern pattern = Pattern.compile(regex);

        System.out.println("Desen başarıyla derlendi: " + pattern.pattern());
    }
}

Matcher Sınıfı: Deseni Uygulama ve Eşleştirme

Matcher sınıfı, derlenmiş bir Pattern nesnesini belirli bir giriş metnine uygulamak ve eşleşmeleri bulmak için kullanılır. Bir Matcher nesnesi, Pattern sınıfının matcher() metodu aracılığıyla oluşturulur. Matcher nesnesi, metin içindeki eşleşmeleri bulmak, bunların konumlarını belirlemek ve eşleşen alt dizeleri çıkarmak gibi işlemleri gerçekleştiren bir dizi metoda sahiptir.


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexEslestirme {
    public static void main(String[] args) {
        String metin = "Bugün java öğreniyorum. Java çok eğlenceli.";
        String regex = "java";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(metin);

        // Metin içinde eşleşme olup olmadığını kontrol ediyoruz
        while (matcher.find()) {
            System.out.println("Eşleşme bulundu: '" + matcher.group() + "' başlangıç indeksi: " + matcher.start() + ", bitiş indeksi: " + matcher.end());
        }
    }
}

Bu örnekte matcher.find() metodu, metinde bir sonraki eşleşmeyi arar. Bulduğunda true döner ve eşleşen metni matcher.group(), başlangıç indeksini matcher.start() ve bitiş indeksini matcher.end() ile alabiliriz. Eğer eşleşme bulunamazsa false döner ve döngü sona erer.

Temel Regex Sözdizimi ve Karakter Sınıfları

Düzenli ifadelerin gücü, kullandığı özel karakterlerde ve bunların kombinasyonlarındadır. Bu karakterlere “meta karakterler” denir.

Literal Karakterler ve Meta Karakterler

Çoğu karakter, kendisini temsil eder (örneğin, ‘a’ harfi ‘a’ harfini eşleştirir). Bunlara literal karakterler denir. Ancak bazı karakterler özel anlamlara sahiptir:

  • . (nokta): Yeni satır karakteri (\n) hariç herhangi bir karakteri eşleştirir.
  • \ (ters eğik çizgi): Bir meta karakterin özel anlamını iptal etmek veya özel bir karakteri belirtmek için kullanılır (kaçış karakteri). Örneğin, \. gerçek bir noktayı eşleştirir.
  • ^: Metnin başlangıcını veya satırın başlangıcını eşleştirir.
  • $: Metnin sonunu veya satırın sonunu eşleştirir.
  • *: Önceki karakterin sıfır veya daha fazla tekrarını eşleştirir.
  • +: Önceki karakterin bir veya daha fazla tekrarını eşleştirir.
  • ?: Önceki karakterin sıfır veya bir tekrarını eşleştirir (isteğe bağlı).
  • {n}, {n,}, {n,m}: Nicelik belirleyiciler (Quantifiers).
  • []: Karakter sınıfları.
  • |: VEYA operatörü (alternatif).
  • (): Gruplama ve yakalama.

Karakter Sınıfları (Character Classes)

Karakter sınıfları, bir karakter kümesinden herhangi bir karakteri eşleştirmek için kullanılır. Köşeli parantezler [] içine alınırlar.

  • [abc]: ‘a’, ‘b’ veya ‘c’ karakterlerinden herhangi birini eşleştirir.
  • [^abc]: ‘a’, ‘b’ veya ‘c’ dışındaki herhangi bir karakteri eşleştirir.
  • [a-z]: Küçük harfli ‘a’dan ‘z’ye kadar herhangi bir karakteri eşleştirir.
  • [A-Z]: Büyük harfli ‘A’dan ‘Z’ye kadar herhangi bir karakteri eşleştirir.
  • [0-9]: ‘0’dan ‘9’a kadar herhangi bir rakamı eşleştirir.
  • [a-zA-Z0-9]: Alfanümerik karakterleri eşleştirir.

Önceden Tanımlanmış Karakter Sınıfları

Yaygın kullanılan karakter sınıfları için kısa yollar mevcuttur:

  • \d: Herhangi bir rakamı eşleştirir ([0-9] ile aynı).
  • \D: Rakam olmayan herhangi bir karakteri eşleştirir ([^0-9] ile aynı).
  • \s: Herhangi bir boşluk karakterini eşleştirir (boşluk, tab, yeni satır vb.).
  • \S: Boşluk olmayan herhangi bir karakteri eşleştirir.
  • \w: Herhangi bir kelime karakterini eşleştirir (harf, rakam veya alt çizgi: [a-zA-Z0-9_]).
  • \W: Kelime olmayan herhangi bir karakteri eşleştirir.

Java’da \ karakteri özel bir anlama sahip olduğundan, düzenli ifade içinde \ kullanmak istediğimizde bunu Java string’i içinde \\ olarak belirtmeliyiz. Örneğin, \d ifadesi Java’da "\\d" olarak yazılmalıdır.

Nicelik Belirleyiciler (Quantifiers)

Nicelik belirleyiciler, bir karakterin veya grubun kaç kez tekrar edebileceğini belirtir.

  • X: X’in sıfır veya daha fazla kez tekrarı. (Örn: a -> “”, “a”, “aa”, “aaa”…)
  • X+: X’in bir veya daha fazla kez tekrarı. (Örn: a+ -> “a”, “aa”, “aaa”…)
  • X?: X’in sıfır veya bir kez tekrarı (isteğe bağlı). (Örn: a? -> “”, “a”)
  • X{n}: X’in tam olarak n kez tekrarı. (Örn: a{3} -> “aaa”)
  • X{n,}: X’in en az n kez tekrarı. (Örn: a{2,} -> “aa”, “aaa”, “aaaa”…)
  • X{n,m}: X’in en az n, en fazla m kez tekrarı. (Örn: a{2,4} -> “aa”, “aaa”, “aaaa”)

Greedy, Reluctant ve Possessive Quantifiers

Nicelik belirleyiciler varsayılan olarak “greedy” (açgözlü) davranış sergiler. Yani mümkün olan en uzun eşleşmeyi bulmaya çalışırlar.
Örneğin, <.> deseni metin metninde metin ifadesinin tamamını eşleştirir, çünkü . mümkün olan en fazla karakteri tüketir.

  • Greedy (Açgözlü): Varsayılan davranıştır. Mümkün olan en fazla karakteri eşleştirir. (*, +, ?, {n,m})
  • Reluctant/Lazy (İstekli olmayan/Tembel): Nicelik belirleyicinin sonuna ? eklenerek elde edilir. Mümkün olan en az karakteri eşleştirir. (*?, +?, ??, {n,m}?)

    Örn: <.*?> deseni metin metninde önce sonra ifadelerini ayrı ayrı eşleştirir.

  • Possessive (Sahiplenici): Nicelik belirleyicinin sonuna + eklenerek elde edilir. Mümkün olan en fazla karakteri eşleştirir, ancak bir kez eşleştikten sonra geri izleme (backtracking) yapmaz. Bu, bazı durumlarda performansı artırabilir ancak eşleşme bulma yeteneğini kısıtlayabilir. (*+, ++, ?+, {n,m}+)

Sınırlar (Anchors) ve Gruplar (Groups)

Düzenli ifadeler, metin içindeki belirli konumlara (sınırlara) veya karakter gruplarına odaklanmamızı sağlayan ek özellikler sunar.

Sınırlar (Anchors)

Sınırlar, eşleşmenin metin içinde belirli bir konumda gerçekleşmesini sağlar, ancak kendileri herhangi bir karakteri eşleştirmezler.

  • ^: Satırın veya metnin başlangıcını eşleştirir. (Multiline modu açık değilse metnin başlangıcıdır.)
  • $: Satırın veya metnin sonunu eşleştirir. (Multiline modu açık değilse metnin sonudur.)
  • \b: Kelime sınırını eşleştirir. Bir harf, rakam veya alt çizgi karakteri ile kelime olmayan bir karakter arasındaki geçişi veya metnin başlangıç/sonunu belirtir.
  • \B: Kelime sınırı olmayan bir konumu eşleştirir. Yani, kelime karakterleri arasında veya kelime olmayan karakterler arasında bir konumu belirtir.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexSinirlar {
    public static void main(String[] args) {
        String metin = "elma, armut, elmasuyu, elma.";
        Pattern pattern = Pattern.compile("\\belma\\b"); // Yalnızca 'elma' kelimesini eşleştir
        Matcher matcher = pattern.matcher(metin);

        while (matcher.find()) {
            System.out.println("Kelime eşleşmesi bulundu: " + matcher.group());
        }
        // Çıktı: Kelime eşleşmesi bulundu: elma
        //         Kelime eşleşmesi bulundu: elma
        // "elmasuyu" içindeki "elma" eşleşmez.
    }
}

Gruplar (Groups) ve Geri Referanslar (Backreferences)

Parantezler () bir grup oluşturmak için kullanılır. Gruplar, birden fazla karakteri tek bir birim olarak ele almamızı ve eşleşen alt dizeleri yakalamamızı sağlar.

  • Yakalama Grupları (Capturing Groups): (desen) şeklinde tanımlanır. Bu gruplar, eşleşen alt dizeleri hafızaya alır ve daha sonra bunlara geri referanslarla veya Matcher.group(int group) metoduyla erişilebilir. Gruplar soldan sağa doğru 1’den başlayarak numaralandırılır. Grup 0, her zaman tüm eşleşmeyi temsil eder.
  • Yakalama Yapmayan Gruplar (Non-capturing Groups): (?:desen) şeklinde tanımlanır. Bunlar da bir grup oluşturur, ancak eşleşen alt dizeleri hafızaya almazlar. Bu, özellikle performans açısından veya sadece gruplama amacıyla kullanışlıdır.

Geri referanslar, bir desen içinde daha önce yakalanan bir gruba referans vermek için kullanılır. \1, \2 gibi ifadelerle belirtilirler.


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexGruplar {
    public static void main(String[] args) {
        String metin = "İsim: Ali Can, Yaş: 30; İsim: Ayşe Yılmaz, Yaş: 25";
        // İsim ve Soyisim gruplarını yakala
        String regex = "İsim: (\\w+) (\\w+), Yaş: (\\d+)";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(metin);

        while (matcher.find()) {
            System.out.println("Tüm eşleşme: " + matcher.group(0));
            System.out.println("İsim: " + matcher.group(1));
            System.out.println("Soyisim: " + matcher.group(2));
            System.out.println("Yaş: " + matcher.group(3));
            System.out.println("---");
        }

        // Geri referans örneği: Tekrar eden kelimeleri bulma
        String tekrarEdenMetin = "Bu bir test test cümlesidir.";
        Pattern tekrarPattern = Pattern.compile("\\b(\\w+)\\s+\\1\\b"); // Aynı kelimenin tekrarı
        Matcher tekrarMatcher = tekrarPattern.matcher(tekrarEdenMetin);

        while (tekrarMatcher.find()) {
            System.out.println("Tekrar eden kelime grubu bulundu: " + tekrarMatcher.group());
            System.out.println("Tekrar eden kelime: " + tekrarMatcher.group(1));
        }
    }
}

Java’da Pattern Sınıfı Detayları

Pattern sınıfı sadece deseni derlemekle kalmaz, aynı zamanda desenin davranışını değiştiren bayraklar (flags) ve bazı kullanışlı yardımcı metotlar sunar.

Pattern.compile() ve Bayraklar (Flags)

Pattern.compile() metodunun ikinci bir parametresi vardır: flags. Bu bayraklar, eşleştirme işleminin nasıl yapılacağını kontrol eder.

  • Pattern.CASE_INSENSITIVE (veya Pattern.I): Büyük/küçük harf duyarsız eşleştirme yapar.
  • Pattern.MULTILINE (veya Pattern.M): ^ ve $ metnin tamamı yerine her satırın başlangıcı ve sonuyla eşleşmesini sağlar.
  • Pattern.DOTALL (veya Pattern.S): . meta karakterinin yeni satır karakteri (\n) dahil tüm karakterleri eşleştirmesini sağlar.
  • Pattern.UNICODE_CASE (veya Pattern.U): Unicode karakterler için büyük/küçük harf duyarsız eşleştirmeyi etkinleştirir.
  • Pattern.COMMENTS (veya Pattern.X): Desende boşluk karakterlerini ve # ile başlayan yorumları göz ardı eder. Bu, karmaşık desenleri daha okunabilir hale getirmek için kullanışlıdır.

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class PatternFlags {
    public static void main(String[] args) {
        String metin = "java\nJava\nJAVA";
        String regex = "java";

        // Büyük/küçük harf duyarsız eşleştirme
        Pattern patternInsensitive = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcherInsensitive = patternInsensitive.matcher(metin);
        System.out.println("Büyük/küçük harf duyarsız eşleşmeler:");
        while (matcherInsensitive.find()) {
            System.out.println("Bulundu: " + matcherInsensitive.group());
        }

        System.out.println("\nMultiline ve Dotall örneği:");
        String cokSatirliMetin = "İlk satır\nİkinci satır";
        // ^ ve $ her satırın başında/sonunda eşleşsin
        Pattern multiLinePattern = Pattern.compile("^İk", Pattern.MULTILINE);
        Matcher multiLineMatcher = multiLinePattern.matcher(cokSatirliMetin);
        System.out.println("Multiline eşleşmeler:");
        while (multiLineMatcher.find()) {
            System.out.println("Bulundu (Multiline): " + multiLineMatcher.group());
        }

        // . yeni satır karakterini de eşleşsin
        Pattern dotAllPattern = Pattern.compile("satır.İk", Pattern.DOTALL);
        Matcher dotAllMatcher = dotAllPattern.matcher(cokSatirliMetin);
        System.out.println("Dotall eşleşmeleri:");
        while (dotAllMatcher.find()) {
            System.out.println("Bulundu (Dotall): " + dotAllMatcher.group());
        }
    }
}

Pattern.matches() ve Pattern.split() Metotları

  • Pattern.matches(String regex, CharSequence input): Bu statik metot, tüm giriş metninin belirli bir desenle tam olarak eşleşip eşleşmediğini kontrol etmek için hızlı bir yoldur. Tek seferlik, tam eşleşme kontrolü için idealdir. Pattern nesnesi oluşturmaya gerek kalmaz.
  • Pattern.split(CharSequence input): Bir giriş metnini, desene göre ayırır ve bir String dizisi döndürür.
  • Pattern.split(CharSequence input, int limit): Aynı işlevi görür, ancak döndürülecek dizinin maksimum boyutunu belirten bir limit parametresi alır.

import java.util.regex.Pattern;
import java.util.Arrays;

public class PatternYardimciMetotlar {
    public static void main(String[] args) {
        // Pattern.matches() örneği
        String email = "test@example.com";
        String invalidEmail = "test@example";
        String emailRegex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b";

        System.out.println("'" + email + "' geçerli mi? " + Pattern.matches(emailRegex, email));
        System.out.println("'" + invalidEmail + "' geçerli mi? " + Pattern.matches(emailRegex, invalidEmail));

        // Pattern.split() örneği
        String cumle = "Bu bir, örnek, cümle, metin.";
        Pattern virgulleAyirici = Pattern.compile(",\\s*"); // Virgül ve ardından gelen boşlukları ayırıcı olarak kullan
        String[] kelimeler = virgulleAyirici.split(cumle);
        System.out.println("\nAyırma işlemi: " + Arrays.toString(kelimeler));

        String cumleLimit = "bir,iki,üç,dört,beş";
        String[] sinirliKelimeler = virgulleAyirici.split(cumleLimit, 3); // En fazla 3 parça
        System.out.println("Sınırlı ayırma işlemi: " + Arrays.toString(sinirliKelimeler));
    }
}

Java’da Matcher Sınıfı Detayları

Matcher sınıfı, eşleştirme sonuçlarını yönetmek ve metin üzerinde daha karmaşık işlemler yapmak için çeşitli metotlar sunar.

Eşleşme Metotları: find(), matches(), lookingAt()

  • boolean find(): Giriş dizisinde desenin bir sonraki eşleşmesini bulmaya çalışır. Birden fazla eşleşmeyi bulmak için bir döngü içinde kullanılır.
  • boolean matches(): Giriş dizisinin tamamının desenle eşleşip eşleşmediğini kontrol eder. Eğer girişin sadece bir kısmı eşleşiyorsa false döner.
  • boolean lookingAt(): Giriş dizisinin başlangıcının desenle eşleşip eşleşmediğini kontrol eder. Girişin tamamının eşleşmesi gerekmez, sadece başlangıcının eşleşmesi yeterlidir.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MatcherMetotlari {
    public static void main(String[] args) {
        String metin = "Merhaba dünya! Java programlama.";
        Pattern pattern = Pattern.compile("Java");
        Matcher matcher = pattern.matcher(metin);

        // find() örneği
        System.out.println("find() ile eşleşme: " + matcher.find()); // true
        System.out.println("Bulunan: " + matcher.group()); // Java

        // matches() örneği (yeni bir matcher nesnesi oluşturulmalı veya resetlenmeli)
        Matcher matcher2 = pattern.matcher("Java");
        System.out.println("matches() ile tam eşleşme: " + matcher2.matches()); // true

        Matcher matcher3 = pattern.matcher("Java programlama");
        System.out.println("matches() ile tam eşleşme (kısmi metin): " + matcher3.matches()); // false (tüm metin değil)

        // lookingAt() örneği
        Matcher matcher4 = pattern.matcher("Java programlama");
        System.out.println("lookingAt() ile başlangıç eşleşmesi: " + matcher4.lookingAt()); // true

        Matcher matcher5 = pattern.matcher("Merhaba Java");
        System.out.println("lookingAt() ile başlangıç eşleşmesi (başka bir yerden): " + matcher5.lookingAt()); // false
    }
}

Eşleşme Bilgileri: group(), start(), end()

Bir eşleşme bulunduktan sonra, Matcher nesnesi eşleşme hakkında detaylı bilgi sağlar:

  • String group(): En son bulunan eşleşen alt dizenin tamamını döndürür. (group(0) ile aynı).
  • String group(int group): Belirtilen yakalama grubunun eşleşen alt dizesini döndürür.
  • int start(): En son bulunan eşleşmenin başlangıç indeksini döndürür.
  • int end(): En son bulunan eşleşmenin bitiş indeksinden bir fazlasını döndürür.
  • int start(int group): Belirtilen yakalama grubunun başlangıç indeksini döndürür.
  • int end(int group): Belirtilen yakalama grubunun bitiş indeksinden bir fazlasını döndürür.

Değiştirme Metotları: replaceAll(), replaceFirst()

Matcher sınıfı, metin içindeki eşleşmeleri başka bir metinle değiştirmek için güçlü metotlar sunar.

  • String replaceAll(String replacement): Giriş dizisindeki desenle eşleşen tüm alt dizeleri belirtilen replacement string’i ile değiştirir.
  • String replaceFirst(String replacement): Giriş dizisindeki desenle eşleşen ilk alt dizeyi belirtilen replacement string’i ile değiştirir.

replacement string’i içinde $1, $2 gibi ifadeler kullanarak yakalama gruplarına referans verebilirsiniz.


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexDegistirme {
    public static void main(String[] args) {
        String metin = "Merhaba dünya! Java programlama çok güzel. Java her yerde.";
        Pattern pattern = Pattern.compile("Java");

        // replaceFirst() örneği
        Matcher matcherFirst = pattern.matcher(metin);
        String ilkDegisim = matcherFirst.replaceFirst("Python");
        System.out.println("İlk değişim: " + ilkDegisim); // Merhaba dünya! Python programlama çok güzel. Java her yerde.

        // replaceAll() örneği
        Matcher matcherAll = pattern.matcher(metin);
        String tumDegisim = matcherAll.replaceAll("C#");
        System.out.println("Tüm değişim: " + tumDegisim); // Merhaba dünya! C# programlama çok güzel. C# her yerde.

        // Yakalama grupları ile değiştirme
        String isimSoyisim = "Adı: Ali, Soyadı: Can";
        Pattern adSoyadPattern = Pattern.compile("Adı: (\\w+), Soyadı: (\\w+)");
        Matcher adSoyadMatcher = adSoyadPattern.matcher(isimSoyisim);
        String formatliIsim = adSoyadMatcher.replaceAll("Tam Adı: $1 $2");
        System.out.println("Formatlı isim: " + formatliIsim); // Tam Adı: Ali Can
    }
}

Pratik Java Regex Örnekleri

Şimdiye kadar öğrendiklerimizi pekiştirmek için bazı yaygın kullanım senaryolarına yönelik pratik örneklere göz atalım.

E-posta Doğrulama

E-posta adreslerinin belirli bir formata uyup uymadığını kontrol etmek, en yaygın Regex kullanım alanlarından biridir. Unutulmamalıdır ki, bir e-posta adresini mükemmel bir şekilde doğrulayan tek bir regex deseni yoktur ve bu desenler oldukça karmaşık olabilir. Aşağıdaki örnek, çoğu standart e-posta formatı için yeterli olacaktır.


import java.util.regex.Pattern;

public class EmailDogrulama {
    private static final String EMAIL_REGEX =
            "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$"; // Daha basit ve yaygın bir regex

    public static boolean isValidEmail(String email) {
        return Pattern.matches(EMAIL_REGEX, email);
    }

    public static void main(String[] args) {
        System.out.println("test@example.com geçerli mi? " + isValidEmail("test@example.com")); // true
        System.out.println("user.name+tag@sub.domain.co.uk geçerli mi? " + isValidEmail("user.name+tag@sub.domain.co.uk")); // true
        System.out.println("invalid-email geçerli mi? " + isValidEmail("invalid-email")); // false
        System.out.println("test@.com geçerli mi? " + isValidEmail("test@.com")); // false
        System.out.println("test@example geçerli mi? " + isValidEmail("test@example")); // false
    }
}

Telefon Numarası Doğrulama (Örn: Türkiye için)

Türkiye’deki telefon numaraları genellikle 0 (5xx) xxx xx xx veya +90 (5xx) xxx xx xx formatındadır. Basit bir doğrulama deseni yazalım.


import java.util.regex.Pattern;

public class TelefonDogrulama {
    // 0 (5xx) xxx xx xx veya +90 (5xx) xxx xx xx formatı
    private static final String TELEFON_REGEX =
            "^(0|\\+90)\\s\\(?\\d{3}\\)?\\s\\d{3}\\s\\d{2}\\s\\d{2}$";

    public static boolean isValidPhoneNumber(String phoneNumber) {
        return Pattern.matches(TELEFON_REGEX, phoneNumber);
    }

    public static void main(String[] args) {
        System.out.println("0 (532) 123 45 67 geçerli mi? " + isValidPhoneNumber("0 (532) 123 45 67")); // true
        System.out.println("+905321234567 geçerli mi? " + isValidPhoneNumber("+905321234567")); // true
        System.out.println("05321234567 geçerli mi? " + isValidPhoneNumber("05321234567")); // true
        System.out.println("1234567 geçerli mi? " + isValidPhoneNumber("1234567")); // false
        System.out.println("+90 (532) 123-45-67 geçerli mi? " + isValidPhoneNumber("+90 (532) 123-45-67")); // false (tire yok)
    }
}

URL Doğrulama

URL’ler de karmaşık bir yapıya sahiptir. İşte basit bir URL doğrulama örneği.


import java.util.regex.Pattern;

public class URLDogrulama {
    private static final String URL_REGEX =
            "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$";

    public static boolean isValidURL(String url) {
        return Pattern.matches(URL_REGEX, url);
    }

    public static void main(String[] args) {
        System.out.println("https://www.google.com geçerli mi? " + isValidURL("https://www.google.com")); // true
        System.out.println("http://example.org/path/to/page?query=string geçerli mi? " + isValidURL("http://example.org/path/to/page?query=string")); // true
        System.out.println("ftp://ftp.test.net/file.zip geçerli mi? " + isValidURL("ftp://ftp.test.net/file.zip")); // true
        System.out.println("www.invalid.com geçerli mi? " + isValidURL("www.invalid.com")); // false (protokol yok)
        System.out.println("invalid url geçerli mi? " + isValidURL("invalid url")); // false
    }
}

Metinden Sayıları Ayıklama

Bir metin içindeki tüm sayısal değerleri bulmak ve ayıklamak.


import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SayiAyiklama {
    public static void main(String[] args) {
        String metin = "Ürün fiyatı 125.50 TL. Stokta 300 adet var. İndirim oranı %10.";
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)?"); // Tam sayı veya ondalıklı sayıları eşleştir
        Matcher matcher = pattern.matcher(metin);

        System.out.println("Metinden ayıklanan sayılar:");
        while (matcher.find()) {
            System.out.println(matcher.group());
        }
    }
}

HTML Etiketlerini Temizleme (Basit)

Basit HTML etiketlerini bir metinden kaldırmak için bir regex kullanabiliriz. Ancak unutulmamalıdır ki, karmaşık HTML ayrıştırma işlemleri için DOM ayrıştırıcıları (JSoup gibi) kullanmak daha güvenli ve sağlamdır.


import java.util.regex.Pattern;

public class HTMLTemizleme {
    public static void main(String[] args) {
        String htmlMetin = "

Bu bir örnek HTML metnidir.

"; // veya formatındaki etiketleri eşleştir Pattern pattern = Pattern.compile("<[^>]+>"); String temizMetin = pattern.matcher(htmlMetin).replaceAll(""); System.out.println("Orijinal HTML: " + htmlMetin); System.out.println("Temizlenmiş Metin: " + temizMetin); } }

Performans ve Dikkat Edilmesi Gerekenler

Düzenli ifadeler güçlüdür, ancak yanlış kullanıldığında performans sorunlarına veya beklenmedik davranışlara yol açabilir.

Pattern.compile()‘ı Döngü Dışında Kullanın

Bir Pattern nesnesini derlemek maliyetli bir işlemdir. Eğer aynı deseni birden fazla kez kullanacaksanız, deseni bir kez derleyip Pattern nesnesini yeniden kullanmalısınız. Özellikle döngü içinde Pattern.compile() çağırmaktan kaçının.


// KÖTÜ ÖRNEK: Her döngüde derleme
for (String metin : metinListesi) {
    Pattern pattern = Pattern.compile("\\d+"); // Her seferinde derlenir
    Matcher matcher = pattern.matcher(metin);
    // ...
}

// İYİ ÖRNEK: Bir kez derleme
Pattern pattern = Pattern.compile("\\d+"); // Sadece bir kez derlenir
for (String metin : metinListesi) {
    Matcher matcher = pattern.matcher(metin);
    // ...
}

Aşırı Geri İzleme (Catastrophic Backtracking)

Bazı düzenli ifadeler, belirli girişlerle karşılaştığında çok fazla geri izleme (backtracking) yaparak performans sorunlarına yol açabilir. Bu durum, özellikle iç içe nicelik belirleyicilerle ((a+)) veya aşırı esnek eşleşmelerle (.) ortaya çıkar. Bu, programın takılmasına veya çok uzun sürmesine neden olabilir (ReDoS – Regular Expression Denial of Service).
Bunu önlemek için:

  • Mümkün olduğunca spesifik desenler kullanın.
  • Açgözlü (greedy) nicelik belirleyiciler yerine isteksiz (reluctant/lazy) veya sahiplenici (possessive) nicelik belirleyicileri deneyin.
  • İç içe * veya + kullanımından kaçının.

Örnek: (a+)+ deseni aaaaaaaaaaaaaaaaaaaaaaaaaab gibi bir metinde çok fazla geri izleme yapabilir.

Karmaşık Desenlerin Okunabilirliği

Düzenli ifadeler, özellikle karmaşık hale geldiklerinde okunması ve bakımı zor olabilir. Pattern.COMMENTS bayrağını kullanarak desenlerinizi yorumlarla daha okunabilir hale getirebilirsiniz.


// Yorumlu regex örneği (Pattern.COMMENTS ile)
String complexRegex = "(?x)" + // COMMENTS bayrağını etkinleştir
                      "^" +    // Satırın başlangıcı
                      "(\\d{3})" + // 3 haneli alan kodu (Grup 1)
                      "\\s*" +   // Sıfır veya daha fazla boşluk
                      "(\\d{3})" + // 3 haneli ilk kısım (Grup 2)
                      "[- .]?" + // Opsiyonel ayırıcı (tire, boşluk veya nokta)
                      "(\\d{4})" + // 4 haneli son kısım (Grup 3)
                      "$";     // Satırın sonu
Pattern pattern = Pattern.compile(complexRegex, Pattern.COMMENTS);

Güvenlik: Kullanıcı Girdisiyle Regex Oluşturma

Kullanıcı tarafından sağlanan girdiyi doğrudan bir düzenli ifade deseni olarak kullanmak güvenlik riski oluşturabilir. Kötü niyetli bir kullanıcı, programınızı DoS saldırılarına açık hale getirebilecek aşırı geri izleme desenleri enjekte edebilir. Mümkünse, kullanıcı girdisini doğrudan desen olarak kullanmaktan kaçının veya girdiyi dikkatlice temizleyin/doğrulayın.

Sonuç

Java’daki düzenli ifadeler, metin işleme görevleri için inanılmaz derecede güçlü ve esnek bir araçtır. java.util.regex paketindeki Pattern ve Matcher sınıfları, desenleri derlemek, metinlerde eşleşmeleri bulmak, yakalama gruplarına erişmek ve metinleri değiştirmek için kapsamlı bir API sunar. Temel sözdiziminden (meta karakterler, nicelik belirleyiciler, karakter sınıfları) ileri seviye özelliklere (gruplar, sınırlar, bayraklar) kadar geniş bir yelpazeyi kapsayan düzenli ifadeler, veri doğrulama, ayrıştırma ve metin manipülasyonu gibi birçok senaryoda geliştiricilerin vazgeçilmez yardımcısıdır.

Ancak, bu gücü kullanırken performans etkilerini, özellikle aşırı geri izleme riskini göz önünde bulundurmak önemlidir. Düzenli ifadeleri etkin bir şekilde kullanmak, pratik ve deneyim gerektirir. Bu makaledeki örnekler ve açıklamalar, Java’da düzenli ifadelerle çalışmaya başlamanız için sağlam bir temel oluşturacaktır. Regex dünyasına daldıkça, metin işleme yeteneklerinizin ne kadar arttığını göreceksiniz.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.