- Karakter sınıfları, niceleyiciler ve çapalarla kalıp yazmak
- Gruplar ve adlandırılmış gruplarla metinden parçalar çıkarmak
- Açgözlü ve tembel niceleyicileri ayırt etmek
re.sub,re.split, bayraklar vere.compileile metin işlemek
Uzun bir metindeki tüm telefon numaralarını bul, bir e-posta adresinin geçerli görünüp görünmediğini denetle, bir milyon satırlık günlük dosyasından tarihleri ve hata düzeylerini çıkar. find() ve split() ile her görev bir sayfalık kırılgan kod ister. Düzenli ifade (regex), aradığın metnin biçimini kısa bir kalıpla tanımlar; aramayı ise re modülü yapar. Düzenli ifadeler her yerde kullanılır: metin düzenleyicilerde, veritabanlarında, komut satırı araçlarında ve web sitelerindeki form doğrulamalarında.
İlk adımlar: search, findall ve ham dizeler
Kalıp, küçük ve özel bir dille yazılmış bir karakter dizisidir. re.search(pattern, text) metnin herhangi bir yerindeki ilk eşleşmeyi bulur ve bir Match nesnesi (ya da None) döndürür; .group() eşleşen metni verir. re.findall ise tüm eşleşmelerin listesini döndürür. \d “bir rakam”, + ise “bir ya da daha fazla” demektir:
import re
text = 'Order 17 shipped on 2026-09-15, order 245 on 2026-09-20.'
print(re.search(r'\d+', text))
print(re.search(r'\d+', text).group())
print(re.findall(r'\d+', text))
print(re.findall(r'\d{4}-\d{2}-\d{2}', text))
print(re.search(r'cancelled', text))▸ Beklenen çıktı
<re.Match object; span=(6, 8), match='17'> 17 ['17', '2026', '09', '15', '245', '2026', '09', '20'] ['2026-09-15', '2026-09-20'] None
Kalıpların dili
| Simge | Anlamı | Örnek |
|---|---|---|
. | yeni satır dışında herhangi bir karakter | a.c → abc, a-c |
\d, \w, \s | rakam, sözcük karakteri (harf, rakam, _), boşluk karakteri | \d\d → 42 |
\D, \W, \S | tersi: rakam değil, sözcük karakteri değil, boşluk değil | \S+ → 2026-09-15 |
[abc], [a-z], [^0-9] | kümeden / aralıktan bir karakter, kümede olmayan bir karakter | [aeiou] → e |
*, +, ? | 0 ya da daha fazla, 1 ya da daha fazla, 0 ya da 1 | colou?r → color, colour |
{n}, {n,m} | tam n kez, n ile m arası tekrar | \d{4} → 2026 |
^, $, \b | dizenin başı ve sonu, sözcük sınırı | ^\d+$ |
a|b, (...) | seçenek, grup | (cat|dog)s |
\., \(, \+ | özel karakterin kendisi: nokta, parantez, artı | \+994 |
import re
print(re.findall(r'\w+', 'Salam, dünya! Привет, мир!'))
print(re.findall(r'\b\w{4}\b', 'Baku is a large city by the sea'))
print(re.findall(r'colou?r', 'color or colour?'))
print(re.findall(r'[A-Z]\w*', 'Aysel and Murad met Leyla'))▸ Beklenen çıktı
['Salam', 'dünya', 'Привет', 'мир'] ['Baku', 'city'] ['color', 'colour'] ['Aysel', 'Murad', 'Leyla']
Python 3'te \w Unicode'u anlar: her alfabenin harfleriyle eşleşir; bu yüzden Azerbaycanca, Türkçe ve Kiril alfabesiyle yazılmış sözcükler de bulunur. \b, bir sözcük karakteri ile sözcük olmayan bir karakter arasındaki görünmez sınırla eşleşir; bu nedenle \b\w{4}\b daha uzun sözcüklerin parçalarını değil, dört harfli tam sözcükleri bulur.
Gruplar ve adlandırılmış gruplar
Parantezler bir grup oluşturur: eşleşmenin gerçekten ihtiyaç duyduğun kısmı. m.group(n) n numaralı grubu döndürür (0 numaralı grup tüm eşleşmedir), m.groups() ise hepsini bir demet olarak verir. Numaralı gruplar çabucak kafa karıştırır; bu yüzden onlara (?P<name>...) ile ad ver ve m['name'] şeklinde ya da hepsini birden m.groupdict() ile oku. re.match yalnızca dizenin başında dener; tek bir günlük satırı için tam uygundur:
import re
line = '2026-09-15 14:02:11 ERROR [payment] card declined'
m = re.match(r'(\S+) (\S+) (\w+) \[(\w+)\] (.*)', line)
print(m.group(3), '|', m.group(5))
print(m.groups())
named = r'(?P<date>\S+) (?P<time>\S+) (?P<level>\w+) \[(?P<module>\w+)\] (?P<msg>.*)'
m = re.match(named, line)
print(m['level'], m['module'])
print(m.groupdict())▸ Beklenen çıktı
ERROR | card declined
('2026-09-15', '14:02:11', 'ERROR', 'payment', 'card declined')
ERROR payment
{'date': '2026-09-15', 'time': '14:02:11', 'level': 'ERROR', 'module': 'payment', 'msg': 'card declined'}Kalıpta gruplar varsa findall tüm eşleşmeyi değil grupları döndürür; birden fazla grup varsa demetler hâlinde. Konumlara ya da adlandırılmış gruplara da ihtiyacın varsa, Match nesnelerini tembelce üreten **finditer** ile döngü kur:
import re
phones = 'Aysel: +994 50 123 45 67, Murad: +994 55 765 43 21'
print(re.findall(r'\+994 (\d{2}) (\d{3} \d{2} \d{2})', phones))
for m in re.finditer(r'(?P<name>\w+): (?P<phone>\+994[\d ]+\d)', phones):
print(m['name'], '->', m['phone'], m.span())▸ Beklenen çıktı
[('50', '123 45 67'), ('55', '765 43 21')]
Aysel -> +994 50 123 45 67 (0, 24)
Murad -> +994 55 765 43 21 (26, 50)Açgözlü ve tembel niceleyiciler
Niceleyiciler varsayılan olarak açgözlüdür (greedy): .+ alabildiği kadar çok metin alır ve karakterleri ancak kalıbın geri kalanı eşleşmeyecekse geri verir. Sonuna ? eklersen niceleyici tembel olur (.+?, *?, ??): olabildiğince az alır. \1 bir geri başvurudur: 1. grupla aynı metinle eşleşmelidir; bu yüzden kapanış etiketi açılış etiketine uyar:
import re
html = '<b>bold</b> and <i>italic</i>'
print(re.findall(r'<.+>', html))
print(re.findall(r'<.+?>', html))
print(re.findall(r'<(\w+)>(.*?)</\1>', html))▸ Beklenen çıktı
['<b>bold</b> and <i>italic</i>']
['<b>', '</b>', '<i>', '</i>']
[('b', 'bold'), ('i', 'italic')]Değiştirme, bölme, bayraklar ve compile
re.sub(pattern, replacement, text) tüm eşleşmeleri değiştirir. Yerine konacak dizede \1, \2… grupları ekler; dize yerine, Match nesnesini alıp yeni metni döndüren bir fonksiyon da verebilirsin. re.split metni bir kalıba göre böler; örneğin virgül, noktalı virgül ve boşlukların herhangi bir karışımına göre:
import re
text = 'Call 050-123-45-67 or 055-765-43-21 today'
print(re.sub(r'\d{3}-\d{3}-\d{2}-\d{2}', '[hidden]', text))
print(re.sub(r'(\d{3})-(\d{3})-(\d{2})-(\d{2})', r'(\1) \2 \3 \4', text))
print(re.sub(r'\d+', lambda m: str(int(m.group()) * 2), 'a1 b22 c333'))
print(re.split(r'[,;\s]+', 'tea, coffee; juice water'))▸ Beklenen çıktı
Call [hidden] or [hidden] today Call (050) 123 45 67 or (055) 765 43 21 today a2 b44 c666 ['tea', 'coffee', 'juice', 'water']
Bayraklar bir kalıbın çalışmasını değiştirir: re.IGNORECASE (re.I) büyük-küçük harf ayrımını yok sayar, re.MULTILINE (re.M) ^ ve $'ın her satırın başı ve sonuyla eşleşmesini sağlar, re.DOTALL (re.S) .'nın yeni satırla da eşleşmesine izin verir, re.VERBOSE (re.X) ise kalıp içinde boşluklara ve # yorumlarına olanak tanır. Birçok kez kullanılan bir kalıp re.compile ile bir kez derlenebilir:
import re
EMAIL = re.compile(r'''
[\w.+-]+ # user name
@
[\w-]+(\.[\w-]+)+ # domain with at least one dot
''', re.VERBOSE)
for address in ['leyla@mail.az', 'elvin.m@edu.gov.az', 'wrong@site', 'no at.com']:
print(f'{address:20} {bool(EMAIL.fullmatch(address))}')
log = 'ERROR one\ninfo two\nError three'
print(re.findall(r'^error.*$', log, re.IGNORECASE | re.MULTILINE))▸ Beklenen çıktı
leyla@mail.az True elvin.m@edu.gov.az True wrong@site False no at.com False ['ERROR one', 'Error three']
re.match— yalnızca dizenin başında dener.re.search— herhangi bir yerdeki ilk eşleşmeyi bulur.re.fullmatch— dizenin tamamı eşleşmelidir.re.findall/re.finditer— tüm eşleşmeler (liste / Match nesnelerinin yineleyicisi).re.sub,re.split— değiştirme ve bölme.
Bir kayıt formunda telefon tam olarak +994 50 123 45 67 biçiminde yazılmalıdır: ülke kodu, iki basamaklı operatör kodu ve tek boşluklarla ayrılmış 3 + 2 + 2 rakam. +994 501234567 ve +994 50 123 45 67 ext reddedilmelidir. Kalıbı kur.
Çözümü gösterÇözümü gizle
+ özel bir karakterdir; bu yüzden \+994 yazarız.Operatör kodu
\d{2}, diğer parçalar \d{3}, \d{2}, \d{2}'dir; aralarında tam bir boşluk vardır.Kalıp:
r'\+994 \d{2} \d{3} \d{2} \d{2}'.re.fullmatch dizenin tamamının eşleşmesini ister; bu yüzden sondaki ext numarayı geçersiz kılar.Metindeki DD.MM.YYYY biçimindeki tüm tarihlerin listesini yazdır, ardından her tarihi YYYY-MM-DD biçimine çevirerek metni yeniden yazdır.
import re
text = 'Exams: 15.06.2026 (math), 18.06.2026 (physics), 22.06.2026 (history).'
# 1) print the list of all dates DD.MM.YYYY
# 2) print the text with every date rewritten as YYYY-MM-DD▸ Beklenen çıktı
['15.06.2026', '18.06.2026', '22.06.2026'] Exams: 2026-06-15 (math), 2026-06-18 (physics), 2026-06-22 (history).
key = value satırlarından (eşittir işaretinin çevresinde boşluk olabilir de olmayabilir de) bir {anahtar: değer} sözlüğü oluştur ve yazdır. # ile başlayan yorum satırları atlanmalıdır.
import re
config = 'host = localhost\nport=8080\n# comment\ndebug = true'
# build a dict {key: value} from the 'key = value' lines and print it▸ Beklenen çıktı
{'host': 'localhost', 'port': '8080', 'debug': 'true'}Önemli noktalar
- Kalıpları ham dize
r'...'olarak yaz;searchilk eşleşmeyi,findallhepsini bulur,fullmatchise dizenin tamamını denetler. - Kalıplar
\d \w \s,[...]sınıfları,* + ? {n,m}niceleyicileri ve^ $ \bçapalarıyla kurulur. - Gruplar
(...)ve adlandırılmış gruplar(?P<name>...)parçaları çıkarır:m.group(1),m['name'],m.groupdict(). - Niceleyiciler açgözlüdür; tembel eşleşme için
?ekle (.+?). re.sub,\1geri başvurularını ve fonksiyonları kabul eder;re.I,re.M,re.S,re.Xbayrakları eşleşmeyi değiştirir.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
r'\d+' gibi ham dize olarak yazarız?