Regular Expressions (re)
Giriş: Metin Arama Süper Gücü
Elinde 10.000 satırlık bir log dosyası var. İçinden sadece hatalı isteklerin IP adreslerini bulmak istiyorsun. Ya da bir formdan gelen email adreslerinin gerçekten geçerli olup olmadığını kontrol etmek istiyorsun. Ya da bir metindeki tüm telefon numaralarını bulmak...
Bu tür metin işleme görevleri için if "x" in text yetmez. Daha güçlü bir araca ihtiyacın var. İşte Regular Expressions (düzenli ifadeler, kısaca regex) bu araç.
Analoji: Metin Dedektifi
Regex'i bir dedektifin profil çizimi gibi düşün. Dedektif diyor ki: "Şüpheli 3 rakamla başlıyor, ardından bir tire geliyor, sonra 4 rakam var." Bu profil çizimine uyan tüm "şüpheliler" (metinler) yakalanır. Regex de tam olarak böyle çalışır — bir pattern (kalıp) tanımlarsın, Python o kalıba uyan tüm metinleri bulur.
re Modülü: Temel Fonksiyonlar
Python'da regex işlemleri re modülü ile yapılır.
import rere.search(): İlk Eşleşmeyi Bul
import re
text = "Bugün hava 25 derece, yarın 28 derece olacak"
match = re.search(r"\d+", text)
if match:
print(match.group()) # 25 (ilk bulunan sayı)
print(match.start()) # 11 (başlangıç indeksi)
print(match.end()) # 13 (bitiş indeksi)
print(match.span()) # (11, 13) (başlangıç, bitiş)
r"\d+"ifadesindekirprefix'i raw string demek. Backslash'lerin Python tarafından yorumlanmasını engeller. Regex yazarken her zamanr""kullan.
re.match(): Sadece Başlangıçta Ara
# match() sadece string'in BAŞINDA arar
text = "Python 3.12 harika"
match = re.match(r"Python", text)
print(match.group() if match else "Yok") # Python
match = re.match(r"harika", text)
print(match.group() if match else "Yok") # Yok (başta değil!)re.findall(): Tüm Eşleşmeleri Bul
text = "Ali 25 yaşında, Veli 30 yaşında, Ayşe 22 yaşında"
numbers = re.findall(r"\d+", text)
print(numbers) # ['25', '30', '22']findall() en sık kullanacağın fonksiyon. Tüm eşleşmeleri bir liste olarak döner.
re.finditer(): Eşleşmeleri Iterator Olarak Al
text = "Python 3.12 ve Java 21 ve Go 1.21"
for match in re.finditer(r"\d+\.?\d*", text):
print(f"Bulundu: {match.group()} pozisyon: {match.span()}")
# Bulundu: 3.12 pozisyon: (7, 11)
# Bulundu: 21 pozisyon: (20, 22)
# Bulundu: 1.21 pozisyon: (29, 33)Büyük metinlerde findall() yerine finditer() tercih et — bellek dostu çalışır.
re.sub(): Bul ve Değiştir
text = "Bugün 25 derece, yarın 28 derece"
result = re.sub(r"\d+", "XX", text)
print(result) # Bugün XX derece, yarın XX derece
# Fonksiyon ile değiştirme
def celsius_to_fahrenheit(match):
celsius = int(match.group())
return str(int(celsius * 9/5 + 32))
result = re.sub(r"\d+", celsius_to_fahrenheit, text)
print(result) # Bugün 77 derece, yarın 82 derecere.split(): Pattern ile Böl
text = "Ali,Veli;Ayşe:Mehmet"
parts = re.split(r"[,;:]", text)
print(parts) # ['Ali', 'Veli', 'Ayşe', 'Mehmet']
# Birden fazla boşlukla böl
text = "Python Java Go Rust"
words = re.split(r"\s+", text)
print(words) # ['Python', 'Java', 'Go', 'Rust']Temel Pattern'ler
Meta Karakterler
| Karakter | Anlam | Örnek | Eşleşir | ||
|---|---|---|---|---|---|
. | Herhangi bir karakter (\n hariç) | a.c | abc, a1c, a-c | ||
* | 0 veya daha fazla tekrar | ab*c | ac, abc, abbc | ||
+ | 1 veya daha fazla tekrar | ab+c | abc, abbc (ac değil!) | ||
? | 0 veya 1 tekrar | colou?r | color, colour | ||
[] | Karakter kümesi | [aeiou] | a, e, i, o, u | ||
() | Grup | (ab)+ | ab, abab, ababab | ||
| `\ | ` | VEYA | `cat\ | dog` | cat, dog |
^ | Satır başı | ^Hello | "Hello..." | ||
$ | Satır sonu | world$ | "...world" | ||
\ | Escape | \. | . (literal nokta) |
Pratik Örnekler
import re
# . (herhangi karakter)
print(re.findall(r"p.t", "pat pet pit pot put"))
# ['pat', 'pet', 'pit', 'pot', 'put']
# * (0 veya daha fazla)
print(re.findall(r"go*gle", "ggle gogle google gooogle"))
# ['ggle', 'gogle', 'google', 'gooogle']
# + (1 veya daha fazla)
print(re.findall(r"go+gle", "ggle gogle google gooogle"))
# ['gogle', 'google', 'gooogle'] — ggle YOK
# ? (0 veya 1)
print(re.findall(r"colou?r", "color colour"))
# ['color', 'colour']
# | (VEYA)
print(re.findall(r"cat|dog", "I have a cat and a dog"))
# ['cat', 'dog']
# ^ ve $
print(re.search(r"^Python", "Python is great")) # Match
print(re.search(r"^Python", "I love Python")) # None
print(re.search(r"great$", "Python is great")) # MatchKarakter Sınıfları
Önceden Tanımlı Sınıflar
| Pattern | Anlam | Eşdeğer |
|---|---|---|
\d | Rakam | [0-9] |
\D | Rakam olmayan | [^0-9] |
\w | Kelime karakteri | [a-zA-Z0-9_] |
\W | Kelime karakteri olmayan | [^a-zA-Z0-9_] |
\s | Boşluk | [ \t\n\r\f\v] |
\S | Boşluk olmayan | [^ \t\n\r\f\v] |
\b | Kelime sınırı | (özel) |
text = "Tarih: 2024-01-15, Fiyat: $99.50, Kod: ABC-123"
# \d — rakamlar
print(re.findall(r"\d+", text))
# ['2024', '01', '15', '99', '50', '123']
# \w — kelime karakterleri
print(re.findall(r"\w+", text))
# ['Tarih', '2024', '01', '15', 'Fiyat', '99', '50', 'Kod', 'ABC', '123']
# \s — boşluklar
print(re.split(r"\s+", "Python Java\tGo\nRust"))
# ['Python', 'Java', 'Go', 'Rust']\b — Kelime Sınırı
text = "cat category caterpillar scattered"
# \b olmadan: her yerde "cat" bulur
print(re.findall(r"cat", text))
# ['cat', 'cat', 'cat', 'cat']
# \b ile: sadece bağımsız "cat" kelimesini bulur
print(re.findall(r"\bcat\b", text))
# ['cat']
# Kelime başında "cat"
print(re.findall(r"\bcat\w*", text))
# ['cat', 'category', 'caterpillar']Özel Karakter Kümeleri
# [abc] — a, b veya c
print(re.findall(r"[aeiou]", "Hello World"))
# ['e', 'o', 'o']
# [a-z] — küçük harfler
# [A-Z] — büyük harfler
# [0-9] — rakamlar
# [a-zA-Z] — tüm harfler
# [^abc] — a, b, c HARİCİ her şey
print(re.findall(r"[^aeiou\s]", "Hello World"))
# ['H', 'l', 'l', 'W', 'r', 'l', 'd']
# Özel karakterler [] içinde escape gerekmez (çoğu)
print(re.findall(r"[.!?]", "Merhaba! Nasılsın? İyi."))
# ['.', '!', '?'] — . burada literal noktaQuantifier'lar: Tekrar Sayısı
Temel Quantifier'lar
| Pattern | Anlam | Örnek |
|---|---|---|
{n} | Tam n kez | \d{3} → 123 |
{n,} | En az n kez | \d{2,} → 12, 123, 1234 |
{n,m} | n ile m arası | \d{2,4} → 12, 123, 1234 |
* | 0+ (= {0,}) | a* |
+ | 1+ (= {1,}) | a+ |
? | 0 veya 1 (= {0,1}) | a? |
# Tam 3 rakam
print(re.findall(r"\d{3}", "12 345 6789 01"))
# ['345', '678']
# 2 ile 4 arası rakam
print(re.findall(r"\d{2,4}", "1 22 333 4444 55555"))
# ['22', '333', '4444', '5555']
# Telefon numarası pattern'ı
text = "Tel: 0532-123-4567 veya 0555-987-6543"
phones = re.findall(r"\d{4}-\d{3}-\d{4}", text)
print(phones) # ['0532-123-4567', '0555-987-6543']Greedy vs Lazy Quantifier'lar
Bu konu çok önemli. Default olarak quantifier'lar greedy (açgözlü) — mümkün olan en uzun eşleşmeyi bulur.
text = "<b>kalın</b> ve <i>italik</i>"
# Greedy (default): .* mümkün olduğunca çok karakter yakalar
match = re.search(r"<.*>", text)
print(match.group())
# <b>kalın</b> ve <i>italik</i> — Çok fazla yakaladı!
# Lazy: .*? mümkün olduğunca AZ karakter yakalar
match = re.search(r"<.*?>", text)
print(match.group())
# <b> — Sadece ilk tag'ı yakaladı
# Tüm tag'ları bul (lazy)
print(re.findall(r"<.*?>", text))
# ['<b>', '</b>', '<i>', '</i>']Lazy quantifier yapmak için ? eklersin:
*?→ lazy *+?→ lazy +??→ lazy ?{n,m}?→ lazy {n,m}
⚠️ Dikkat: HTML/XML parse etmek için regex kullanma! Bunun için
BeautifulSoupveyalxmlgibi kütüphaneler var. Yukarıdaki örnek sadece greedy/lazy farkını göstermek için.
Gruplar: () ile Yakalama
Parantez () iki iş yapar:
Pattern'leri gruplar (birlikte davranması için)
Eşleşen kısmı yakalar (sonra erişebilirsin)
Temel Gruplama
# Gruplama: (ab)+ ile "ab" tekrarını ara
print(re.findall(r"(ab)+", "ababab cd abab"))
# ['ab', 'ab'] — findall() grup içeriğini döner!
# Tüm eşleşmeyi görmek için:
for m in re.finditer(r"(?:ab)+", "ababab cd abab"):
print(m.group())
# ababab
# ababYakalama Grupları
# Tarih parse etme
text = "Tarih: 2024-01-15"
match = re.search(r"(\d{4})-(\d{2})-(\d{2})", text)
if match:
print(match.group()) # 2024-01-15 (tüm eşleşme)
print(match.group(1)) # 2024 (1. grup)
print(match.group(2)) # 01 (2. grup)
print(match.group(3)) # 15 (3. grup)
print(match.groups()) # ('2024', '01', '15')findall() ve Gruplar
# findall() grup varsa grup içeriklerini döner
text = "Ali:25, Veli:30, Ayşe:22"
pairs = re.findall(r"(\w+):(\d+)", text)
print(pairs)
# [('Ali', '25'), ('Veli', '30'), ('Ayşe', '22')]
for name, age in pairs:
print(f"{name} → {age} yaşında")Non-Capturing Group: (?:...)
Bazen gruplamak istiyorsun ama yakalamak istemiyorsun:
# Yakalayan grup
print(re.findall(r"(https?)://(\w+)", "http://google https://python"))
# [('http', 'google'), ('https', 'python')]
# Non-capturing grup
print(re.findall(r"(?:https?)://(\w+)", "http://google https://python"))
# ['google', 'python'] — sadece yakalayan grup dönerNamed Groups: (?P<name>...)
Gruplara numara yerine isim verebilirsin. Kodu çok daha okunaklı yapar.
# Named group ile tarih parse etme
text = "Doğum tarihi: 1990-05-15"
pattern = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
match = re.search(pattern, text)
if match:
print(match.group("year")) # 1990
print(match.group("month")) # 05
print(match.group("day")) # 15
print(match.groupdict()) # {'year': '1990', 'month': '05', 'day': '15'}Named Group ile sub()
# Tarih formatını değiştir: YYYY-MM-DD → DD/MM/YYYY
text = "Tarihler: 2024-01-15, 2024-06-30"
result = re.sub(
r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})",
r"\g<d>/\g<m>/\g<y>",
text
)
print(result)
# Tarihler: 15/01/2024, 30/06/2024Log Parser Örneği
log_line = '[2024-01-15 14:30:22] ERROR: Connection timeout (host=db.example.com)'
pattern = (
r"\[(?P<timestamp>[\d-]+ [\d:]+)\] "
r"(?P<level>\w+): "
r"(?P<message>.+)"
)
match = re.search(pattern, log_line)
if match:
data = match.groupdict()
print(data)
# {'timestamp': '2024-01-15 14:30:22', 'level': 'ERROR',
# 'message': 'Connection timeout (host=db.example.com)'}re.compile(): Pattern'ı Önceden Derleme
Aynı pattern'ı birçok kez kullanacaksan, re.compile() ile önceden derle. Performans kazancı sağlar.
# Her seferinde derleme (yavaş)
for line in log_lines:
match = re.search(r"\d{4}-\d{2}-\d{2}", line)
# Önceden derleme (hızlı)
date_pattern = re.compile(r"\d{4}-\d{2}-\d{2}")
for line in log_lines:
match = date_pattern.search(line)Compiled pattern nesnesi aynı metodlara sahip:
email_pattern = re.compile(r"[\w.+-]+@[\w-]+\.[\w.]+")
# search, findall, sub, split hepsi var
emails = email_pattern.findall("ali@gmail.com ve veli@hotmail.com")
print(emails) # ['ali@gmail.com', 'veli@hotmail.com']
# match
result = email_pattern.match("test@example.com")
print(result.group()) # test@example.com💡 İpucu: Python regex'leri zaten dahili olarak cache'ler (son 512 pattern). Ama açıkça
compile()kullanmak kodu daha okunaklı yapar ve IDE desteği sağlar.
Flags: Regex Davranışını Değiştirme
re.IGNORECASE (re.I)
# Büyük/küçük harf farkını yok say
print(re.findall(r"python", "Python PYTHON python", re.IGNORECASE))
# ['Python', 'PYTHON', 'python']
# Kısa yazım
print(re.findall(r"python", "Python PYTHON python", re.I))
# ['Python', 'PYTHON', 'python']re.MULTILINE (re.M)
text = """Birinci satır
İkinci satır
Üçüncü satır"""
# Default: ^ sadece string başı
print(re.findall(r"^\w+", text))
# ['Birinci']
# MULTILINE: ^ her satır başı
print(re.findall(r"^\w+", text, re.MULTILINE))
# ['Birinci', 'İkinci', 'Üçüncü']re.DOTALL (re.S)
text = "Satır 1\nSatır 2\nSatır 3"
# Default: . newline'ı eşleştirmez
match = re.search(r"Satır 1.+Satır 3", text)
print(match) # None
# DOTALL: . newline'ı da eşleştirir
match = re.search(r"Satır 1.+Satır 3", text, re.DOTALL)
print(match.group()) # Satır 1\nSatır 2\nSatır 3re.VERBOSE (re.X) — Okunaklı Regex
# Karmaşık regex'i okunaklı yaz
email_pattern = re.compile(r"""
^ # String başı
[\w.+-]+ # Kullanıcı adı (harf, rakam, ., +, -)
@ # @ işareti
[\w-]+ # Domain adı
\. # Nokta
[\w.]+ # TLD (.com, .co.uk vs.)
$ # String sonu
""", re.VERBOSE)
print(email_pattern.match("ali@gmail.com")) # Match
print(email_pattern.match("geçersiz@")) # NoneBirden Fazla Flag
# Flag'leri | ile birleştir
pattern = re.compile(r"^hello\s+world$", re.IGNORECASE | re.MULTILINE)Pratik: Email Validator
import re
def validate_email(email):
"""Email adresini doğrula."""
pattern = re.compile(r"""
^ # Başlangıç
[a-zA-Z0-9._%+-]+ # Kullanıcı adı
@ # @ işareti
[a-zA-Z0-9.-]+ # Domain
\. # Nokta
[a-zA-Z]{2,} # TLD (en az 2 karakter)
$ # Bitiş
""", re.VERBOSE)
return bool(pattern.match(email))
# Test
test_emails = [
"ali@gmail.com", # ✅ Geçerli
"veli.yilmaz@company.co", # ✅ Geçerli
"test+tag@example.org", # ✅ Geçerli
"@gmail.com", # ❌ Kullanıcı adı yok
"ali@", # ❌ Domain yok
"ali@gmail", # ❌ TLD yok
"ali gmail.com", # ❌ @ yok
]
for email in test_emails:
status = "✅" if validate_email(email) else "❌"
print(f"{status} {email}")Pratik: Telefon Numarası Parser
def parse_phone_numbers(text):
"""Metindeki telefon numaralarını bul ve normalize et."""
pattern = re.compile(r"""
(?: # Ülke kodu (opsiyonel)
(?:\+90|0090) # +90 veya 0090
\s* # Boşluk (opsiyonel)
)?
\(? # Açık parantez (opsiyonel)
(5\d{2}) # Operatör kodu: 5XX
\)? # Kapalı parantez (opsiyonel)
[\s.-]? # Ayırıcı (opsiyonel)
(\d{3}) # İlk 3 rakam
[\s.-]? # Ayırıcı (opsiyonel)
(\d{2}) # Sonraki 2 rakam
[\s.-]? # Ayırıcı (opsiyonel)
(\d{2}) # Son 2 rakam
""", re.VERBOSE)
results = []
for match in pattern.finditer(text):
op, p1, p2, p3 = match.groups()
normalized = f"0{op} {p1} {p2} {p3}"
results.append({
"original": match.group(),
"normalized": normalized
})
return results
# Test
text = """
Bana ulaşın: 0532-123-45-67
Alternatif: (555) 987 65 43
Yurtdışından: +90 542 111 22 33
Veya: 05301234567
"""
for phone in parse_phone_numbers(text):
print(f"{phone['original']:>25} → {phone['normalized']}")Pratik: Log Parser
def parse_log_file(log_text):
"""Log dosyasını parse et."""
pattern = re.compile(r"""
\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\] # Tarih
\s+
(?P<level>DEBUG|INFO|WARNING|ERROR|CRITICAL) # Seviye
\s+
(?P<module>[\w.]+) # Modül
\s*:\s*
(?P<message>.+) # Mesaj
""", re.VERBOSE)
entries = []
for line in log_text.strip().split("\n"):
match = pattern.search(line)
if match:
entries.append(match.groupdict())
return entries
log_text = """
[2024-01-15 14:30:22] INFO app.server: Server started on port 8080
[2024-01-15 14:30:25] DEBUG app.db: Connected to database
[2024-01-15 14:31:00] ERROR app.auth: Authentication failed for user admin
[2024-01-15 14:31:05] WARNING app.cache: Cache miss for key user:123
[2024-01-15 14:32:00] CRITICAL app.server: Out of memory!
"""
entries = parse_log_file(log_text)
for entry in entries:
print(f"[{entry['level']:>8}] {entry['module']}: {entry['message']}")
# Sadece hataları filtrele
errors = [e for e in entries if e["level"] in ("ERROR", "CRITICAL")]
print(f"\nToplam hata: {len(errors)}")İleri Regex Teknikleri
Lookahead ve Lookbehind
Bunlar eşleşmeyi kontrol eder ama yakalamaz (zero-width assertions):
# Positive lookahead: (?=...) — sonrasında ... olan
# "Python" kelimesini bul AMA sadece sonrasında sayı varsa
text = "Python3 Python Java Python2"
print(re.findall(r"Python(?=\d)", text))
# ['Python', 'Python'] — Python3 ve Python2'deki "Python"
# Negative lookahead: (?!...) — sonrasında ... OLMAYAN
print(re.findall(r"Python(?!\d)", text))
# ['Python'] — sadece yalın "Python"
# Positive lookbehind: (?<=...) — öncesinde ... olan
text = "$100 €200 $300"
print(re.findall(r"(?<=\$)\d+", text))
# ['100', '300'] — $ işaretinden sonraki sayılar
# Negative lookbehind: (?<!...) — öncesinde ... OLMAYAN
print(re.findall(r"(?<!\$)\d+", text))
# ['200'] — $ ile başlamayan sayılar (€200'ün sayısı)Backreference: Önceki Gruba Referans
# \1 ile ilk gruba referans: tekrar eden kelimeler bul
text = "Bu bu bir bir test test cümlesi"
duplicates = re.findall(r"\b(\w+)\s+\1\b", text, re.IGNORECASE)
print(duplicates) # ['bu', 'bir', 'test'] — tekrar eden kelimeler
# HTML tag eşleştirme: açılış ve kapanış tag'ı aynı olmalı
text = "<b>kalın</b> <i>italik</i> <b>hatalı</i>"
valid_tags = re.findall(r"<(\w+)>.*?</\1>", text)
print(valid_tags) # ['b', 'i'] — sadece eşleşen tag'larKoşullu Pattern
# Şifre gücü kontrolü:
# En az 8 karakter, büyük harf, küçük harf, rakam, özel karakter
def check_password(password):
checks = {
"en az 8 karakter": r".{8,}",
"büyük harf": r"[A-Z]",
"küçük harf": r"[a-z]",
"rakam": r"\d",
"özel karakter": r"[!@#$%^&*(),.?\":{}|<>]"
}
results = {}
for desc, pattern in checks.items():
results[desc] = bool(re.search(pattern, password))
return results
result = check_password("MyP@ss123")
for check, passed in result.items():
status = "✅" if passed else "❌"
print(f"{status} {check}")Yaygın Regex Kalıpları
# IP adresi
ip_pattern = r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
# URL
url_pattern = r"https?://[\w.-]+(?:\.[\w]+)+(?:/[\w.-]*)*"
# Tarih (YYYY-MM-DD)
date_pattern = r"\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])"
# Hex renk kodu
hex_color_pattern = r"#(?:[0-9a-fA-F]{3}){1,2}\b"
# Türkçe TC Kimlik No (11 rakam, 0 ile başlamaz)
tc_pattern = r"\b[1-9]\d{10}\b"Yaygın Hatalar
1. Raw String Kullanmamak
# YANLIŞ: \b Python'da backspace
print(re.findall("\btest\b", "this is a test")) # []
# DOĞRU: r prefix ile raw string
print(re.findall(r"\btest\b", "this is a test")) # ['test']2. Greedy Tuzağı
# YANLIŞ: HTML tag'ları arasını yakala
html = "<p>Paragraf 1</p><p>Paragraf 2</p>"
print(re.findall(r"<p>(.*)</p>", html))
# ['Paragraf 1</p><p>Paragraf 2'] — Çok fazla yakaladı!
# DOĞRU: Lazy kullan
print(re.findall(r"<p>(.*?)</p>", html))
# ['Paragraf 1', 'Paragraf 2']3. Özel Karakterleri Escape Etmemek
# YANLIŞ: . herhangi karakter demek
print(re.findall(r"example.com", "exampleXcom example.com"))
# ['exampleXcom', 'example.com']
# DOĞRU: \. literal nokta
print(re.findall(r"example\.com", "exampleXcom example.com"))
# ['example.com']
# re.escape() ile otomatik escape
user_input = "price is $10.00 (USD)"
safe = re.escape(user_input)
print(safe) # price\ is\ \$10\.00\ \(USD\)Özet
Regex, metin içinde karmaşık kalıplara göre arama, eşleştirme ve değiştirme yapmanın güçlü yoludur.
remodülü ile kullanılır.Temel fonksiyonlar:
search()ilk eşleşme,findall()tüm eşleşmeler,sub()bul-değiştir,split()böl,finditer()lazy eşleşme.Karakter sınıfları (
\d,\w,\s,\b) ve quantifier'lar (*,+,?,{n,m}) pattern'lerin yapı taşlarıdır. Greedy vs lazy farkını unutma.Gruplar
()ile yakalama,(?:...)non-capturing,(?P<name>...)named group olarak kullanılır.`re.compile()` ile tekrar kullanılacak pattern'leri önceden derle. Flag'ler (
re.I,re.M,re.S,re.X) davranışı özelleştirir.Regex güçlü ama okuması zor olabilir.
re.VERBOSEile yorum ekle, karmaşık pattern'leri parçala ve her zaman raw string (r"") kullan.
AI Asistan
Sorularını yanıtlamaya hazır