← Kursa Dön
📄 Text · 30 min

Regular Expressions (RegExp)

Neden Bu Konu Önemli?

Bir kütüphanede çalıştığını düşün. Birisi gelip "Başlığında 'JavaScript' geçen, 2020'den sonra yayınlanmış tüm kitapları bul" dese, her kitabı tek tek okuman gerekirdi. Ama elinde sihirli bir filtre olsa — kitaplığa tuttuğunda kriterlere uyan kitaplar otomatik parlasa — iş çok kolaylaşırdı.

Regular expressions (regex/regexp, düzenli ifadeler) tam bu sihirli filtredir. Metin içinde kalıp (pattern) eşleme yapmanı sağlar. Email formatı doğru mu? Telefon numarası geçerli mi? Bir metindeki tüm URL'leri bul. Tarihleri farklı formata çevir. Tüm bu işlemler regex ile birkaç satırda yapılır.

Regex ilk bakışta korkutucu görünebilir — /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ gibi bir ifade hieroglif gibi. Ama her simgenin bir anlamı var ve bu derste hepsini adım adım öğreneceksin.

JavaScript'te regex, String metotlarıyla (match, replace, search, split) ve RegExp nesnesiyle kullanılır. Form validasyonu, veri temizleme, metin işleme, URL parsing — web geliştirmede her yerde karşına çıkar.


Regex Oluşturma

JavaScript'te regex oluşturmanın iki yolu var:

// 1. Regex literal (en yaygın) — iki / arasında
const regex1 = /merhaba/;
const regex2 = /[0-9]+/g;

// 2. RegExp constructor — dinamik regex oluştururken
const aranan = "JavaScript";
const regex3 = new RegExp(aranan, "gi");

// Fark: Literal derleme zamanında oluşturulur (hızlı)
//        Constructor çalışma zamanında oluşturulur (dinamik)

Ne Zaman Hangisi?

// ✅ Literal — sabit pattern'ler için
const emailPattern = /^[\w.+-]+@[\w-]+\.[\w.]+$/;

// ✅ Constructor — kullanıcı girdisinden pattern oluşturmak için
function highlightText(text, searchTerm) {
  // searchTerm kullanıcıdan geliyor — literal'de yazamayız
  const regex = new RegExp(`(${escapeRegExp(searchTerm)})`, "gi");
  return text.replace(regex, "<mark>$1</mark>");
}

// ⚠️ Kullanıcı girdisini regex'e geçirirken escape et!
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

Temel Regex Sözdizimi

Basit Eşleme

const metin = "JavaScript harika bir dil";

// Basit metin eşleme
console.log(/harika/.test(metin));       // true
console.log(/python/.test(metin));       // false (case-sensitive!)
console.log(/python/i.test(metin));      // false (python yok)
console.log(/javascript/i.test(metin));  // true (i = case-insensitive)

Flag'ler (Bayraklar)

// Flag'ler regex'in davranışını değiştirir

const metin = "Ali ali ALİ";

// i → case-insensitive (büyük/küçük harf duyarsız)
console.log(metin.match(/ali/));    // ["ali"] — sadece küçük
console.log(metin.match(/ali/i));   // ["Ali"] — ilk eşleşme (büyük/küçük fark etmez)

// g → global (tüm eşleşmeleri bul)
console.log(metin.match(/ali/g));   // ["ali"] — sadece küçük, tümü
console.log(metin.match(/ali/gi));  // ["Ali", "ali", "ALİ"] — hepsi

// m → multiline (^ ve $ her satıra uygulanır)
const cokSatirli = `Satır 1
Satır 2
Satır 3`;
console.log(cokSatirli.match(/^Satır/g));   // ["Satır"] — sadece ilk satır
console.log(cokSatirli.match(/^Satır/gm));  // ["Satır", "Satır", "Satır"] — her satır

// s → dotAll (. karakteri \n'i de eşleştirir)
// d → hasIndices (eşleşme indexlerini döndürür)

Özel Karakterler

// === Karakter Sınıfları ===

// \d → Rakam [0-9]
// \D → Rakam OLMAYAN [^0-9]
// \w → Kelime karakteri [a-zA-Z0-9_]
// \W → Kelime karakteri OLMAYAN
// \s → Boşluk (space, tab, newline)
// \S → Boşluk OLMAYAN
// .  → Herhangi bir karakter (\n hariç)

const metin = "Telefon: 0532-123-4567";

console.log(metin.match(/\d+/g));
// ["0532", "123", "4567"] — tüm rakam grupları

console.log(metin.match(/\D+/g));
// ["Telefon: ", "-", "-"] — rakam olmayan bölümler

// === Konum Belirteçleri (Anchors) ===

// ^  → Metnin/satırın başı
// $  → Metnin/satırın sonu
// \b → Kelime sınırı (word boundary)

console.log(/^Ali/.test("Ali gel"));     // true — başta "Ali" var
console.log(/^Ali/.test("Gel Ali"));     // false — başta değil
console.log(/gel$/.test("Ali gel"));     // true — sonda "gel" var

// \b — kelime sınırı (çok kullanışlı)
console.log(/\bJava\b/.test("JavaScript")); // false — "Java" ayrı kelime değil
console.log(/\bJava\b/.test("Java is old")); // true — "Java" ayrı kelime

// === Tekrar Belirteçleri (Quantifiers) ===

// *     → 0 veya daha fazla
// +     → 1 veya daha fazla
// ?     → 0 veya 1 (opsiyonel)
// {n}   → Tam n kez
// {n,}  → En az n kez
// {n,m} → En az n, en fazla m kez

console.log(/\d{3}/.test("123"));          // true — 3 rakam
console.log(/\d{3}/.test("12"));           // false — 2 rakam
console.log(/\d{2,4}/.test("12345"));      // true — 2-4 rakam bulunabilir
console.log(/colou?r/.test("color"));      // true — u opsiyonel
console.log(/colou?r/.test("colour"));     // true — u var, OK
console.log(/ha+/.test("haaaa"));          // true — 1+ a
console.log(/ha*/.test("h"));             // true — 0+ a

Karakter Setleri ve Aralıkları

// [...] → Karakter seti — içindeki HERHANGİ BİR karakter
// [^...] → Negatif set — içindekiler HARİÇ

console.log(/[aeiou]/.test("hello"));    // true — sesli harf var
console.log(/[^aeiou]/g.test("aaa"));    // false — sessiz harf yok

// Aralıklar
console.log(/[a-z]/.test("hello"));     // true — küçük harf
console.log(/[A-Z]/.test("Hello"));     // true — büyük harf
console.log(/[0-9]/.test("abc123"));    // true — rakam
console.log(/[a-zA-Z0-9]/.test("!"));   // false — alfanumerik değil

// Birleşik set örnekleri
const hexColor = /^#[0-9a-fA-F]{6}$/;
console.log(hexColor.test("#FF5733"));   // true
console.log(hexColor.test("#ff5733"));   // true
console.log(hexColor.test("#GGGGGG"));   // false — G hex değil
console.log(hexColor.test("FF5733"));    // false — # eksik

Gruplar ve Yakalama

Parantezler () iki amaçla kullanılır: gruplama ve yakalama (capture).

// Gruplama — birden fazla karaktere quantifier uygulamak
console.log(/ha+/.test("haaaa"));      // true — "h" + "aaaa"
console.log(/(ha)+/.test("hahaha"));   // true — "ha" tekrarı

// Yakalama — eşleşen bölümleri çıkarmak
const tarih = "2025-03-15";
const match = tarih.match(/(\d{4})-(\d{2})-(\d{2})/);

console.log(match[0]); // "2025-03-15" — tüm eşleşme
console.log(match[1]); // "2025" — birinci grup (yıl)
console.log(match[2]); // "03" — ikinci grup (ay)
console.log(match[3]); // "15" — üçüncü grup (gün)

// İsimli gruplar (named groups) — ES2018
const match2 = tarih.match(/(?<yil>\d{4})-(?<ay>\d{2})-(?<gun>\d{2})/);
console.log(match2.groups.yil); // "2025"
console.log(match2.groups.ay);  // "03"
console.log(match2.groups.gun); // "15"

Yakalamayan Grup ve Alternation

// (?:...) → Yakalamayan grup (non-capturing) — performans için
const time = "14:30";
const match = time.match(/(?:\d{2}):(\d{2})/);
// (?:\d{2}) yakalanmaz, sadece gruplar
console.log(match[1]); // "30" — dakika (saat yakalanmadı)

// | → Alternation (VEYA)
const hayvan = "kedi";
console.log(/kedi|köpek|kuş/.test(hayvan)); // true

// Grup içinde alternation
const dosya = "script.ts";
console.log(/\.(js|ts|jsx|tsx)$/.test(dosya)); // true — JS/TS dosyası
console.log(/\.(js|ts|jsx|tsx)$/.test("style.css")); // false

String Metotlarıyla Regex

test() — Eşleşme Var mı?

const email = "ali@example.com";
const emailRegex = /^[\w.+-]+@[\w-]+\.[\w.]+$/;

console.log(emailRegex.test(email)); // true
console.log(emailRegex.test("ali@")); // false
console.log(emailRegex.test("@example.com")); // false

match() — Eşleşmeleri Bul

const metin = "Sipariş #1234 ve Sipariş #5678 hazır";

// Flag'siz — ilk eşleşme + detay
const ilk = metin.match(/#(\d+)/);
console.log(ilk[0]); // "#1234" — tüm eşleşme
console.log(ilk[1]); // "1234" — yakalanan grup

// g flag — tüm eşleşmeler (gruplar döndürülmez)
const tumEslesmeler = metin.match(/#\d+/g);
console.log(tumEslesmeler); // ["#1234", "#5678"]

matchAll() — Tüm Eşleşmeler + Gruplar

const metin = "Ali (25), Ayşe (30), Can (22)";
const regex = /(\w+)\s\((\d+)\)/g;

// matchAll — her eşleşme için grup detayları
for (const match of metin.matchAll(regex)) {
  console.log(`İsim: ${match[1]}, Yaş: ${match[2]}`);
}
// İsim: Ali, Yaş: 25
// İsim: Ayşe, Yaş: 30
// İsim: Can, Yaş: 22

replace() ve replaceAll()

// Basit değiştirme
const metin = "merhaba dünya, merhaba JavaScript";

console.log(metin.replace("merhaba", "selam"));
// "selam dünya, merhaba JavaScript" — sadece ilk

console.log(metin.replace(/merhaba/g, "selam"));
// "selam dünya, selam JavaScript" — tümü

// Grup referansı ile değiştirme ($1, $2...)
const tarih = "2025-03-15";
const trFormat = tarih.replace(/(\d{4})-(\d{2})-(\d{2})/, "$3/$2/$1");
console.log(trFormat); // "15/03/2025"

// Fonksiyon ile değiştirme
const fiyatlar = "Elma: 5tl, Armut: 8tl, Portakal: 12tl";
const zamli = fiyatlar.replace(/(\d+)tl/g, (match, fiyat) => {
  return `${Math.round(Number(fiyat) * 1.25)}tl`; // %25 zam
});
console.log(zamli); // "Elma: 6tl, Armut: 10tl, Portakal: 15tl"

// İsimli grup referansı
const isimFormat = "Yılmaz, Ali".replace(
  /(?<soyisim>\w+), (?<isim>\w+)/,
  "$<isim> $<soyisim>"
);
console.log(isimFormat); // "Ali Yılmaz"

split()

// Regex ile split — esnek ayırma
const csv = "Ali,  Ayşe , Can,Deniz";
console.log(csv.split(/\s*,\s*/));
// ["Ali", "Ayşe", "Can", "Deniz"] — boşluklar temizlendi

// Birden fazla ayırıcı
const metin = "elma;armut,portakal|muz";
console.log(metin.split(/[;,|]/));
// ["elma", "armut", "portakal", "muz"]

// Cümlelere böl
const paragraf = "Birinci cümle. İkinci cümle! Üçüncü cümle?";
console.log(paragraf.split(/[.!?]\s*/));
// ["Birinci cümle", "İkinci cümle", "Üçüncü cümle", ""]
// search — ilk eşleşmenin index'ini döndürür, bulamazsa -1
const metin = "Fiyat: 199.99 TL";

console.log(metin.search(/\d/));      // 7 — ilk rakamın indexi
console.log(metin.search(/€/));       // -1 — bulunamadı

Gerçek Dünya Örnekleri

1. Form Validasyonu

// Email validasyonu
function isValidEmail(email) {
  const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return regex.test(email);
}

console.log(isValidEmail("ali@example.com"));     // true
console.log(isValidEmail("ali@example"));          // false — TLD yok
console.log(isValidEmail("ali@.com"));             // false — domain yok
console.log(isValidEmail("@example.com"));         // false — kullanıcı yok

// Telefon numarası (Türkiye formatı)
function isValidPhoneNumber(phone) {
  // 05XX XXX XX XX veya +90 5XX XXX XX XX
  const regex = /^(\+90|0)?5\d{2}[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/;
  return regex.test(phone.trim());
}

console.log(isValidPhoneNumber("0532 123 45 67"));   // true
console.log(isValidPhoneNumber("+905321234567"));     // true
console.log(isValidPhoneNumber("532-123-45-67"));     // true
console.log(isValidPhoneNumber("1234567890"));        // false

// Güçlü şifre kontrolü
function isStrongPassword(password) {
  const checks = {
    minLength: password.length >= 8,
    hasUppercase: /[A-Z]/.test(password),
    hasLowercase: /[a-z]/.test(password),
    hasNumber: /\d/.test(password),
    hasSpecial: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>?]/.test(password),
  };

  const passed = Object.values(checks).filter(Boolean).length;
  return {
    isStrong: passed >= 4,
    score: passed,
    details: checks,
  };
}

console.log(isStrongPassword("Abc123!@"));
// { isStrong: true, score: 5, details: { ... } }

console.log(isStrongPassword("abc"));
// { isStrong: false, score: 1, details: { ... } }

// TC Kimlik numarası format kontrolü (11 haneli rakam)
function isValidTCKN(tckn) {
  return /^\d{11}$/.test(tckn) && tckn[0] !== "0";
}

2. Veri Temizleme ve Dönüştürme

// HTML tag'lerini temizle
function stripHtml(html) {
  return html.replace(/<[^>]*>/g, "");
}
console.log(stripHtml("<p>Merhaba <b>dünya</b></p>"));
// "Merhaba dünya"

// Fazla boşlukları temizle
function normalizeWhitespace(text) {
  return text.replace(/\s+/g, " ").trim();
}
console.log(normalizeWhitespace("  çok   fazla    boşluk  "));
// "çok fazla boşluk"

// camelCase → kebab-case dönüşümü
function camelToKebab(str) {
  return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
console.log(camelToKebab("backgroundColor")); // "background-color"
console.log(camelToKebab("fontSize"));         // "font-size"

// URL'den parametreleri çıkar
function parseQueryString(url) {
  const params = {};
  const regex = /[?&]([^=]+)=([^&]*)/g;

  for (const match of url.matchAll(regex)) {
    params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
  }
  return params;
}

console.log(parseQueryString("https://example.com?name=Ali&age=25&city=Istanbul"));
// { name: "Ali", age: "25", city: "Istanbul" }

// Markdown bold → HTML bold
function markdownBoldToHtml(text) {
  return text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
}
console.log(markdownBoldToHtml("Bu **önemli** bir **mesaj**"));
// "Bu <strong>önemli</strong> bir <strong>mesaj</strong>"

3. Log Analizi

// Apache log satırını parse et
const logLine = '192.168.1.1 - - [15/Mar/2025:14:30:22 +0300] "GET /api/users HTTP/1.1" 200 1234';

const logRegex = /^([\d.]+)\s-\s-\s\[(.+?)\]\s"(\w+)\s(.+?)\sHTTP\/[\d.]+"\s(\d+)\s(\d+)$/;
const match = logLine.match(logRegex);

if (match) {
  const [, ip, timestamp, method, path, status, bytes] = match;
  console.log({ ip, timestamp, method, path, status: Number(status), bytes: Number(bytes) });
}
// { ip: "192.168.1.1", timestamp: "15/Mar/2025:14:30:22 +0300",
//   method: "GET", path: "/api/users", status: 200, bytes: 1234 }

Lookahead ve Lookbehind

İleri ve geri bakış (lookahead/lookbehind), eşleşmeyi tüketmeden "yanına bak" demenizi sağlar:

// Positive Lookahead (?=...) — "sonrasında X olan"
// Negatif Lookahead (?!...) — "sonrasında X OLMAYAN"
// Positive Lookbehind (?<=...) — "öncesinde X olan"
// Negative Lookbehind (?<!...) — "öncesinde X OLMAYAN"

// Positive Lookahead: Sonunda TL yazan sayıları bul
const fiyatlar = "Elma 5TL, Portakal 8$, Armut 3TL";
console.log(fiyatlar.match(/\d+(?=TL)/g)); // ["5", "3"] — TL öncesi sayılar

// Negative Lookahead: TL ile BİTMEYEN sayıları bul
console.log(fiyatlar.match(/\d+(?!TL)/g)); // ["8"] — $ olanı (ve kısmi eşleşmeler)

// Positive Lookbehind: $ işaretinden sonraki sayıları bul
const prices = "Apple $5, Orange $8, Banana $3";
console.log(prices.match(/(?<=\$)\d+/g)); // ["5", "8", "3"]

// Negative Lookbehind: # ile başlamayan renk kodları
const colors = "#FF0000 red 00FF00 #0000FF blue";
console.log(colors.match(/(?<!#)[0-9A-F]{6}/g)); // ["00FF00"]

// Şifre validasyonu — lookahead ile tüm koşulları tek regex'te kontrol
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

console.log(strongPassword.test("Abc123!@")); // true
console.log(strongPassword.test("abcdefgh")); // false — büyük harf ve rakam yok

Greedy vs Lazy Eşleme

Quantifier'lar varsayılan olarak açgözlü (greedy) — mümkün olduğunca çok karakter eşleştirir. ? ekleyerek tembel (lazy) yapabilirsin — mümkün olduğunca az karakter eşleştirir.

const html = "<b>kalın</b> ve <i>italik</i>";

// Greedy (varsayılan) — olabildiğince çok al
console.log(html.match(/<.+>/g));
// ["<b>kalın</b> ve <i>italik</i>"] — her şeyi kaptı!

// Lazy (?  ekle) — olabildiğince az al
console.log(html.match(/<.+?>/g));
// ["<b>", "</b>", "<i>", "</i>"] — sadece tag'ler

// Greedy vs Lazy karşılaştırma
const text = "aabab";
console.log(text.match(/a.*b/));    // ["aabab"] — greedy, en uzun eşleşme
console.log(text.match(/a.*?b/));   // ["aab"] — lazy, en kısa eşleşme

⚠️ Dikkat: HTML parse etmek için regex kullanma! Basit tag temizleme OK ama iç içe, karmaşık HTML için DOM parser (DOMParser) kullan. Regex HTML'in tüm edge case'lerini karşılayamaz.


Performans İpuçları

// 1. Regex'i döngü dışında oluştur (her iterasyonda yeniden derlenmez)

// ❌ Her iterasyonda yeni regex oluşturuluyor
for (const item of list) {
  if (/pattern/.test(item)) { // Her seferinde yeni regex!
    // ...
  }
}

// ✅ Bir kez oluştur, tekrar kullan
const regex = /pattern/;
for (const item of list) {
  if (regex.test(item)) {
    // ...
  }
}

// 2. Alternation'da sık eşleşenleri başa koy
// ❌ Nadir olan başta — her denemede başarısız
const fileRegex = /\.(yaml|toml|xml|json|js|ts)$/;

// ✅ Sık eşleşen başta — erken çıkış
const fileRegex = /\.(js|ts|json|xml|yaml|toml)$/;

// 3. Backtracking'e dikkat et — catastrophic backtracking
// ❌ Tehlikeli pattern — üstel zaman karmaşıklığı
const bad = /^(a+)+$/;
// "aaaaaaaaaaaaaaaaab" gibi bir input ile saniyeler sürebilir!

// ✅ Basitleştir
const good = /^a+$/;

Yaygın Regex Kalıpları (Cheat Sheet)

// Email (basitleştirilmiş)
const email = /^[\w.+-]+@[\w-]+\.[\w.]+$/;

// URL
const url = /^https?:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?$/;

// IP adresi (IPv4)
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;

// Tarih (YYYY-MM-DD)
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;

// Hex renk kodu
const hexColor = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;

// Sadece harf (Türkçe dahil)
const onlyLetters = /^[a-zA-ZçÇğĞıİöÖşŞüÜ]+$/;

// Boşlukları normalize et
const normalizeSpaces = (s) => s.replace(/\s+/g, " ").trim();

// Sayı formatlama (1234567 → 1.234.567)
const formatNumber = (n) =>
  String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ".");
console.log(formatNumber(1234567)); // "1.234.567"

Yaygın Hatalar

1. Özel Karakterleri Escape Etmemek

// ❌ . herhangi bir karakter demek — literal nokta değil!
const regex = /example.com/;
console.log(regex.test("example.com")); // true
console.log(regex.test("exampleXcom")); // true — . her şeyi eşleştirir!

// ✅ Literal nokta için escape et: \.
const regex = /example\.com/;
console.log(regex.test("example.com")); // true
console.log(regex.test("exampleXcom")); // false ✅

2. g Flag ile test() Kullanırken State Sorunu

// ❌ g flag'li regex, test()'te lastIndex'i günceller
const regex = /abc/g;
console.log(regex.test("abc")); // true
console.log(regex.test("abc")); // false! (lastIndex ilerletildi)
console.log(regex.test("abc")); // true (lastIndex sıfırlandı)

// ✅ g flag'i test() ile kullanma — veya her seferinde lastIndex sıfırla
const regex2 = /abc/; // g yok
console.log(regex2.test("abc")); // true
console.log(regex2.test("abc")); // true — tutarlı

3. Greedy Eşleme Tuzağı

// ❌ .* her şeyi yutar
const input = '"Ali" ve "Ayşe"';
console.log(input.match(/".*"/g));
// ['"Ali" ve "Ayşe"'] — tüm metin tek eşleşme!

// ✅ Lazy kullan veya karakter seti ile sınırla
console.log(input.match(/".*?"/g));    // ['"Ali"', '"Ayşe"']
console.log(input.match(/"[^"]*"/g));  // ['"Ali"', '"Ayşe"'] — daha hızlı

Özet

  • Regular expressions metin içinde kalıp (pattern) eşleme yapmanı sağlar. /pattern/flags sözdizimi ile oluşturulur.

  • Temel karakterler: \d (rakam), \w (kelime), \s (boşluk), . (herhangi), ^ (baş), $ (son), \b (kelime sınırı).

  • Quantifier'lar: * (0+), + (1+), ? (0-1), {n} (tam n), {n,m} (n ile m arası). Varsayılan greedy, ? ile lazy.

  • Gruplar: () yakalama, (?:) yakalamayan, (?<name>) isimli grup. $1, $2 ile replace'te referans.

  • Lookahead/Lookbehind: (?=), (?!), (?<=), (?<!) — eşleşmeyi tüketmeden yandaki metni kontrol eder.

  • String metotları: test() (boolean), match() (eşleşme), matchAll() (tüm eşleşmeler + gruplar), replace() (değiştirme), split() (bölme).

  • Pratik kullanım: Form validasyonu, veri temizleme, log analizi, format dönüştürme. Regex web geliştirmenin temel araçlarından biridir.