Məzmuna keç
Educora
İrəli22 dəq20 / 42

Requlyar ifadələr: re modulu

Mətndə şablon üzrə axtarış: `re.search`, `findall`, `finditer`, simvol sinifləri və kəmiyyət göstəriciləri, qruplar və adlı qruplar, acgöz və tənbəl axtarış, `sub`, `split`, bayraqlar və `compile`.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Simvol sinifləri, kəmiyyət göstəriciləri və lövbərlərlə şablon yazmaq
  • Qruplar və adlı qruplarla mətndən hissələr çıxarmaq
  • Acgöz və tənbəl kəmiyyət göstəricilərini fərqləndirmək
  • re.sub, re.split, bayraqlar və re.compile ilə mətni emal etmək

Uzun mətndə bütün telefon nömrələrini tap, e-poçt ünvanının düzgün görünüb-görünmədiyini yoxla, bir milyon sətirlik log faylından tarixləri və xəta səviyyələrini çıxar. find() və split() ilə hər tapşırıq bir səhifəlik kövrək kod tələb edir. Requlyar ifadə (regex) axtardığın mətnin formasını bir qısa şablonla təsvir edir, axtarışı isə re modulu görür. Requlyar ifadələr hər yerdə işlədilir: mətn redaktorlarında, verilənlər bazalarında, əmr sətri alətlərində və saytlardakı formaların yoxlanmasında.

İlk addımlar: search, findall və xam sətirlər

Şablon kiçik xüsusi dildə yazılmış sətirdir. re.search(pattern, text) mətnin istənilən yerində birinci uyğunluğu tapır və Match obyekti (və ya None) qaytarır; .group() tapılan mətni verir. re.findall isə bütün uyğunluqların siyahısını qaytarır. \d «rəqəm», + isə «bir və ya daha çox» deməkdir:

Python
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))
▸ Gözlənilən nəticə
<re.Match object; span=(6, 8), match='17'>
17
['17', '2026', '09', '15', '245', '2026', '09', '20']
['2026-09-15', '2026-09-20']
None

Şablonların dili

SimvolMənasıNümunə
.yeni sətir simvolundan başqa istənilən simvola.c → abc, a-c
\d, \w, \srəqəm, söz simvolu (hərf, rəqəm, _), boşluq simvolu\d\d → 42
\D, \W, \Səksi: rəqəm deyil, söz simvolu deyil, boşluq deyil\S+ → 2026-09-15
[abc], [a-z], [^0-9]çoxluqdan / aralıqdan bir simvol, çoxluqda olmayan simvol[aeiou] → e
*, +, ?0 və ya çox, 1 və ya çox, 0 və ya 1colou?r → color, colour
{n}, {n,m}düz n dəfə, n-dən m-ə qədər təkrar\d{4} → 2026
^, $, \bsətrin əvvəli, sonu, söz sərhədi^\d+$
a|b, (...)alternativ, qrup(cat|dog)s
\., \(, \+xüsusi simvolun özü: nöqtə, mötərizə, üstəgəl\+994
Python
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'))
▸ Gözlənilən nəticə
['Salam', 'dünya', 'Привет', 'мир']
['Baku', 'city']
['color', 'colour']
['Aysel', 'Murad', 'Leyla']

Python 3-də \w Unicode-u başa düşür: istənilən əlifbanın hərflərinə uyğun gəlir, ona görə ə, ü hərfli və kiril sözlər də tapılır. \b söz simvolu ilə qeyri-söz simvolu arasındakı görünməz sərhədə uyğun gəlir, buna görə \b\w{4}\b uzun sözlərin hissələrini yox, məhz dörd hərfli bütöv sözləri tapır.

Qruplar və adlı qruplar

Mötərizələr qrup yaradır — uyğunluğun həqiqətən lazım olan hissəsini. m.group(n) n-ci qrupu qaytarır (0 nömrəli qrup bütün uyğunluqdur), m.groups() isə hamısını kortej kimi verir. Nömrələnmiş qruplar tez çaşdırır, ona görə onlara (?P<name>...) ilə ad ver və m['name'] kimi və ya hamısını birdən m.groupdict() ilə oxu. re.match yalnız sətrin əvvəlindən yoxlayır — bir log sətri üçün tam uyğundur:

Python
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())
▸ Gözlənilən nəticə
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'}

Şablonda qruplar varsa, findall bütün uyğunluğu yox, qrupları qaytarır — qrup bir neçədirsə, kortej şəklində. Mövqelər və ya adlı qruplar da lazımdırsa, Match obyektlərini tənbəlcə verən **finditer** ilə dövr qur:

Python
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())
▸ Gözlənilən nəticə
[('50', '123 45 67'), ('55', '765 43 21')]
Aysel -> +994 50 123 45 67 (0, 24)
Murad -> +994 55 765 43 21 (26, 50)

Acgöz və tənbəl kəmiyyət göstəriciləri

Kəmiyyət göstəriciləri susmaya görə acgözdür (greedy): .+ mümkün qədər çox mətn tutur və simvolları yalnız şablonun qalan hissəsi uyğun gəlmədikdə geri verir. Sonuna ? əlavə etsən, göstərici tənbəl olur (.+?, *?, ??): mümkün qədər az götürür. \1 geri istinaddır — 1-ci qrupla eyni mətnə uyğun gəlməlidir, ona görə bağlayan teq açan teqə uyğun gəlməlidir:

Python
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))
▸ Gözlənilən nəticə
['<b>bold</b> and <i>italic</i>']
['<b>', '</b>', '<i>', '</i>']
[('b', 'bold'), ('i', 'italic')]

Əvəzetmə, bölmə, bayraqlar və compile

re.sub(pattern, replacement, text) bütün uyğunluqları əvəz edir. Əvəzedici sətirdə \1, \2… qrupları yerləşdirir; sətir əvəzinə Match obyektini alıb yeni mətni qaytaran funksiya da vermək olar. re.split mətni şablona görə bölür, məsələn, vergül, nöqtəli vergül və boşluqların istənilən qarışığına görə:

Python
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'))
▸ Gözlənilən nəticə
Call [hidden] or [hidden] today
Call (050) 123 45 67 or (055) 765 43 21 today
a2 b44 c666
['tea', 'coffee', 'juice', 'water']

Bayraqlar şablonun işini dəyişir: re.IGNORECASE (re.I) hərflərin böyük-kiçikliyinə baxmır, re.MULTILINE (re.M) ^ və $-ı hər sətrin əvvəli və sonu edir, re.DOTALL (re.S) .-yə yeni sətir simvolunu da tutmağa icazə verir, re.VERBOSE (re.X) isə şablonun içində boşluqlara və # şərhlərinə imkan yaradır. Dəfələrlə işlədilən şablonu re.compile ilə bir dəfə kompilyasiya etmək olar:

Python
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))
▸ Gözlənilən nəticə
leyla@mail.az        True
elvin.m@edu.gov.az   True
wrong@site           False
no at.com            False
['ERROR one', 'Error three']
  • re.match — yalnız sətrin əvvəlində yoxlayır.
  • re.search — istənilən yerdə birinci uyğunluğu tapır.
  • re.fullmatch — bütün sətir şablona uyğun gəlməlidir.
  • re.findall / re.finditer — bütün uyğunluqlar (siyahı / Match obyektlərinin iteratoru).
  • re.sub, re.split — əvəz etmək və bölmək.
Nümunə: mobil nömrənin formatını yoxlamaq

Qeydiyyat formasında telefon düz +994 50 123 45 67 formatında yazılmalıdır: ölkə kodu, iki rəqəmli operator kodu və 3 + 2 + 2 rəqəm, aralarında bir boşluq. +994 501234567 və +994 50 123 45 67 ext rədd edilməlidir. Şablonu qur.

Həllini göstər
+ xüsusi simvoldur, ona görə \+994 yazılır.
Operator kodu \d{2}, qalan hissələr \d{3}, \d{2}, \d{2}-dir; aralarında düz bir boşluq var.
Şablon: r'\+994 \d{2} \d{3} \d{2} \d{2}'.
re.fullmatch bütün sətrin uyğun gəlməsini tələb edir, ona görə sonundakı ext nömrəni etibarsız edir.
Tapşırıq

Mətndəki DD.MM.YYYY formatlı bütün tarixlərin siyahısını çap et, sonra hər tarixi YYYY-MM-DD formatına çevirib mətni yenidən çap et.

Tapşırıq · Python
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
▸ Gözlənilən nəticə
['15.06.2026', '18.06.2026', '22.06.2026']
Exams: 2026-06-15 (math), 2026-06-18 (physics), 2026-06-22 (history).
Tapşırıq

key = value sətirlərindən (bərabərlik işarəsinin ətrafında boşluq ola da, olmaya da bilər) {açar: qiymət} lüğəti qur və çap et. # ilə başlayan şərh sətirləri nəzərə alınmamalıdır.

Tapşırıq · Python
import re

config = 'host = localhost\nport=8080\n# comment\ndebug =  true'
# build a dict {key: value} from the 'key = value' lines and print it
▸ Gözlənilən nəticə
{'host': 'localhost', 'port': '8080', 'debug': 'true'}

Əsas fikirlər

  • Şablonları xam sətir r'...' kimi yaz; search birinci, findall bütün uyğunluqları tapır, fullmatch isə bütün sətri yoxlayır.
  • Şablonlar \d \w \s, [...] sinifləri, * + ? {n,m} kəmiyyət göstəriciləri və ^ $ \b lövbərlərindən qurulur.
  • Qruplar (...) və adlı qruplar (?P<name>...) hissələri çıxarır: m.group(1), m['name'], m.groupdict().
  • Kəmiyyət göstəriciləri acgözdür; tənbəl axtarış üçün ? əlavə et (.+?).
  • re.sub \1 geri istinadlarını və funksiyaları qəbul edir; re.I, re.M, re.S, re.X bayraqları axtarışı dəyişir.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
Şablonu niyə r'\d+' kimi xam sətirlə yazırıq?