16 β Muntazam ifodalar (regex, re moduli)¶
Tasavvur qil: foydalanuvchi formaga email kiritdi β aziz@mail deb. Bu to'g'ri emailmi? Yoki minglab qatorli log faylidan faqat xato (ERROR) qatorlarini, ulardagi sana va IP manzilni ajratib olish kerak. Bularni oddiy in, .split(), .find() bilan qilish mumkin, lekin tezda halokatga aylanadi β chunki bunday tekshiruvlar naqsh (pattern) ustida ishlaydi: "bir nechta harf, keyin @, keyin domen, keyin nuqta...". Mana shu naqshlarni qisqa, kuchli tilda yozish vositasi β muntazam ifodalar (regular expressions, qisqacha regex), Python'da esa re moduli orqali.
Regex avvaliga "begona alifbo" kabi ko'rinadi (r"^\d{3}-\d{2}$" nimaga o'xshaydi?), lekin u aslida juda mantiqiy. Bu bobda har bir belgini noldan, analogiya bilan tushuntiramiz va har birini real misolda β email, O'zbekiston telefoni, parol, sana, IP, log β ishlatib ko'ramiz.
Bu modulda:
remodulining barcha asosiy funksiyalari (search,match,fullmatch,findall,finditer,sub,split), raw stringr"..."nega zarurligi, naqsh sintaksisi to'liq (. \d \w \s, belgi sinflari, anchorlar, miqdorlar, guruhlar, nomli guruhlar, tanlov, ekranlash), bayroqlar (IGNORECASE,MULTILINE,DOTALL),re.compile, Match obyekti, hamda real validatsiya va parsing misollari.
16.1 Birinchi regex: naqsh bormi yoki yo'qmi?¶
Regex bilan ishlash uchun avval re modulini import qilamiz. Eng oddiy savol: "shu matnda shu naqsh bormi?" Buni re.search() hal qiladi β u matnni boshidan oxirigacha qidiradi va birinchi moslikni topadi.
import re
matn = "Mening yoshim 25 da"
natija = re.search(r"\d+", matn) # \d+ β bir yoki ko'p raqam
print(natija) # <re.Match object; span=(14, 16), match='25'>
print(natija.group()) # 25 β topilgan qism
re.search() ikkita narsa qaytaradi:
- agar moslik topilsa β Match obyekti (uni if da True deb hisoblaydi);
- agar topilmasa β None.
Shuning uchun natijani odatda if bilan tekshiramiz:
import re
def raqam_bormi(matn: str) -> bool:
return re.search(r"\d", matn) is not None
print(raqam_bormi("salom123")) # True
print(raqam_bormi("salom")) # False
Bu yerda \d β "bitta raqam" degani. Naqsh nimani anglatishini keyingi bo'limlarda batafsil ko'ramiz; hozir asosiy oqimni tushunib ol: naqsh + matn β Match yoki None.
16.2 Raw string r"..." β nega regex'da shart?¶
Yuqorida naqshni r"\d+" deb yozdik β boshida r harfi bilan. Bu raw string (xom satr). Nega kerak?
Muammo shundaki, Python'ning oddiy satrida \ (teskari chiziq) maxsus ma'noga ega: \n β yangi qator, \t β tabulyatsiya, \b β backspace. Lekin regex'ning o'zi ham \ dan keng foydalanadi (\d, \w, \b). Natijada chalkashlik chiqadi:
print("\d") # Python ogohlantiradi: \d maxsus emas, lekin xavfli
print("\n") # bu β yangi qator (bo'sh satr chiqadi)
print(r"\n") # bu β ikki belgi: \ va n
Raw string r"..." ichida \ oddiy belgi sifatida qoladi, hech narsa "tarjima" qilinmaydi. Demak r"\d" β aynan \ va d ikki belgisi, va regex buni "raqam" deb to'g'ri o'qiydi.
import re
# r SIZ β \b ni Python "backspace" deb tarjima qiladi, regex buziladi
print(re.findall("\bsom", "1000 som")) # [] β ishlamaydi
# r BILAN β \b regex'ga "so'z chegarasi" bo'lib yetadi
print(re.findall(r"\bsom", "1000 som")) # ['som'] β to'g'ri
Qoida: regex naqshini doim
r"..."bilan yoz. Bu odat seni ko'p tushunarsiz xatolardan qutqaradi.
16.3 search, match, fullmatch β uchta turli savol¶
Uchala funksiya ham naqshni qidiradi, lekin qayerdan qidirishi va qancha qismni talab qilishi bilan farq qiladi. Bu farqni tushunish juda muhim.
re.searchβ naqsh matnning istalgan joyida bormi?re.matchβ naqsh matnning boshidan boshlanadimi? (oxirigacha shart emas)re.fullmatchβ naqsh butun matnga to'liq mos keladimi? (boshidan oxirigacha)
import re
matn = "abc123"
print(re.search(r"\d+", matn)) # match='123' β istalgan joyda topdi
print(re.match(r"\d+", matn)) # None β bosh '1' emas, 'a'
print(re.match(r"[a-z]+", matn)) # match='abc' β bosh harflar bilan boshlandi
print(re.fullmatch(r"[a-z]+", matn)) # None β butun matn faqat harf emas
print(re.fullmatch(r"[a-z]+\d+", matn)) # match='abc123' β to'liq mos
Buni shunday eslab qol: - search β "ichidan top" (lupa bilan qidiradi), - match β "boshidan boshlanadimi", - fullmatch β "aynan shumi" (validatsiya uchun eng qulayi).
Email yoki telefonni tekshirishda odatda re.fullmatch ishlatamiz β chunki bizga "matn butunlay to'g'ri formatdami" kerak, "ichida bormi" emas.
16.4 Pattern asoslari: literal va . (har qanday belgi)¶
Naqshning eng oddiy turi β literal, ya'ni belgilarning o'zi. r"som" naqshi matndagi aynan som ketma-ketligini qidiradi.
Maxsus belgi . (nuqta) β istalgan bitta belgini anglatadi (yangi qatordan tashqari):
import re
print(re.findall(r"k.t", "kot kit kut kt kart"))
# ['kot', 'kit', 'kut']
# k.t = k, istalgan bitta belgi, t
# 'kt' mos kelmadi (o'rtada belgi yo'q), 'kart' ham (ikki belgi)
Agar haqiqiy nuqtani qidirmoqchi bo'lsang (masalan 3.14 dagi nuqta), uni ekranlash kerak β \. deb yozasan. Bu haqda 16.13 da batafsil.
16.5 \d \w \s va ularning teskarilari¶
Regex'da eng ko'p ishlatiladigan maxsus belgi sinflari β bular qisqa yozuvlar:
| Belgi | Ma'nosi | Misol |
|---|---|---|
\d |
raqam (digit) 0-9 |
7, 0 |
\w |
"so'z belgisi": harf, raqam, _ |
a, 5, _ |
\s |
bo'sh joy (space, tab, yangi qator) | , \t |
\D |
raqam emas | a, @ |
\W |
so'z belgisi emas | @, , . |
\S |
bo'sh joy emas | a, 5 |
Katta harfli variant β kichik harflining teskarisi. Ular juda foydali:
import re
matn = "Buyurtma #42 narxi 1500 som"
print(re.findall(r"\d", matn)) # ['4', '2', '1', '5', '0', '0'] β har bir raqam
print(re.findall(r"\d+", matn)) # ['42', '1500'] β raqamlar guruhi
print(re.findall(r"\w+", matn)) # ['Buyurtma', '42', 'narxi', '1500', 'som']
print(re.findall(r"\S+", matn)) # ['Buyurtma', '#42', 'narxi', '1500', 'som']
\d bilan \d+ farqiga e'tibor ber: \d β bitta raqam, \d+ β ketma-ket bir yoki ko'p raqam (miqdorlar haqida 16.8 da).
16.6 Belgi sinflari [...] va inkor [^...]¶
Kvadrat qavs [...] β "shu belgilardan biri" degani. Bu o'zingning maxsus sinfingni yaratish usuli.
import re
# [aiu] β a, i yoki u harflaridan biri
print(re.findall(r"k[aiu]t", "kat kit kut ket kot"))
# ['kat', 'kit', 'kut'] β ket va kot mos emas (e, o yo'q)
Qavs ichida diapazon (-) yozish mumkin:
- [a-z] β kichik lotin harflari,
- [A-Z] β katta harflar,
- [0-9] β raqamlar (\d bilan bir xil),
- [a-zA-Z0-9] β harf yoki raqam.
import re
print(re.findall(r"[a-z]+", "Salom Dunyo 2026")) # ['alom', 'unyo'] β faqat kichik
print(re.findall(r"[A-Za-z]+", "Salom Dunyo 2026")) # ['Salom', 'Dunyo']
print(re.findall(r"[0-9]+", "Salom Dunyo 2026")) # ['2026']
^ belgisi qavs ichida va boshida β inkor ("bulardan tashqari hammasi"):
import re
# [^0-9] β raqam BO'LMAGAN har qanday belgi
print(re.sub(r"[^0-9]", "", "Tel: +998-90-123-45-67"))
# 998901234567 β faqat raqamlar qoldi
Diqqat: qavs ichida ko'p maxsus belgilar (
.,+,*) oddiy belgiga aylanadi.[.+]β aynan nuqta yoki plyus belgisi, miqdor emas.
16.7 Anchorlar: ^, $, \b¶
Anchorlar (langar) belgilar β ular hech narsaga "mos kelmaydi", balki pozitsiyani ko'rsatadi:
^β matn (yoki qator) boshi,$β matn (yoki qator) oxiri,\bβ so'z chegarasi (so'z boshi yoki oxiri).
import re
print(re.search(r"^Salom", "Salom dunyo")) # topadi β boshida 'Salom'
print(re.search(r"^Salom", "Hey, Salom")) # None β boshida emas
print(re.search(r"dunyo$", "Salom dunyo")) # topadi β oxirida 'dunyo'
^...$ birgalikda butun matn shu naqshga to'liq mos kelishini talab qiladi β bu fullmatch ga juda o'xshaydi va validatsiyada ko'p ishlatiladi:
import re
def faqat_raqamlardanmi(s: str) -> bool:
return re.search(r"^\d+$", s) is not None
print(faqat_raqamlardanmi("12345")) # True
print(faqat_raqamlardanmi("123a5")) # False β o'rtada harf bor
\b (so'z chegarasi) β so'zni "yopishgan" matndan ajratib oladi. Masalan som so'zini topish, lekin kosmos ichidagisini emas:
import re
print(re.findall(r"\bsom\b", "som va kosmos somon"))
# ['som'] β faqat alohida 'som' so'zi (kosmos, somon ichidagisi emas)
16.8 Miqdorlar: * + ? {n} {n,m}¶
Miqdorlar β naqshning bir qismi necha marta takrorlanishini ko'rsatadi. Bular o'zidan oldingi elementga tegishli.
| Miqdor | Ma'nosi |
|---|---|
* |
0 yoki ko'p marta |
+ |
1 yoki ko'p marta (kamida bitta) |
? |
0 yoki 1 marta (ixtiyoriy) |
{n} |
aniq n marta |
{n,m} |
n dan m gacha |
{n,} |
n yoki ko'p marta |
import re
print(re.findall(r"go*l", "gl gol goool")) # ['gl', 'gol', 'goool'] β 'o' 0+ marta
print(re.findall(r"go+l", "gl gol goool")) # ['gol', 'goool'] β 'o' 1+ marta
print(re.findall(r"go?l", "gl gol gool")) # ['gl', 'gol'] β 'o' 0 yoki 1
{n} va {n,m} aniq sonni belgilaydi β masalan, telefon kodi yoki pochta indeksi uchun ideal:
import re
# aniq 5 ta raqam (pochta indeksi)
print(re.fullmatch(r"\d{5}", "10000")) # match='10000'
print(re.fullmatch(r"\d{5}", "1000")) # None β 4 ta raqam
# 2 dan 4 gacha raqam
print(re.findall(r"\d{2,4}", "1 22 333 44444")) # ['22', '333', '4444']
Dangasa (lazy) miqdor *? va +?¶
Odatda miqdorlar ochko'z (greedy) β imkon qadar ko'proq belgi yutadi. Ba'zan bu xato natija beradi:
import re
matn = "<b>qalin</b> matn"
# ochko'z: birinchi < dan oxirgi > gacha hammasini oladi
print(re.findall(r"<.*>", matn)) # ['<b>qalin</b>']
# dangasa (*?): imkon qadar KAM oladi β har bir tegni alohida
print(re.findall(r"<.*?>", matn)) # ['<b>', '</b>']
? ni miqdordan keyin qo'ysang (*?, +?, {n,m}?), u dangasa bo'ladi β minimal mos kelishni oladi. HTML teglari, qo'shtirnoq ichidagi matnni ajratishda juda foydali.
16.9 findall va finditer β barcha mosliklar¶
re.search faqat birinchi moslikni topadi. Barchasini olish uchun ikki yo'l bor.
re.findall β barcha mosliklarni ro'yxat (list) sifatida qaytaradi (matn ko'rinishida):
import re
matn = "Narxlar: 1500, 23000 va 450 som"
print(re.findall(r"\d+", matn)) # ['1500', '23000', '450']
re.finditer β barcha mosliklarni Match obyektlari ko'rinishida, birma-bir (iterator) qaytaradi. Bu pozitsiya (.start(), .end()) ham kerak bo'lganda ishlatiladi:
import re
matn = "Narxlar: 1500, 23000 va 450 som"
for m in re.finditer(r"\d+", matn):
print(f"{m.group()} β pozitsiya {m.start()}-{m.end()}")
# 1500 β pozitsiya 9-13
# 23000 β pozitsiya 15-20
# 450 β pozitsiya 24-27
Qaysi birini tanlash? Faqat topilgan matnlar kerak bo'lsa β
findall(oddiy). Pozitsiya yoki guruhlar bilan murakkab ishlov kerak bo'lsa βfinditer.
findall ning bir o'ziga xosligi: agar naqshda guruh ((...)) bo'lsa, u butun moslik o'rniga faqat guruhlarni qaytaradi (bu haqda 16.10 da).
16.10 Guruhlar (...) β qismni ajratib olish¶
Qavs (...) naqshning bir qismini guruhga oladi β keyin o'sha qismni alohida ajratib olish mumkin. Bu regex'ning eng kuchli imkoniyatlaridan biri.
import re
matn = "Sana: 2026-06-11"
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", matn)
print(m.group()) # 2026-06-11 β butun moslik (group 0)
print(m.group(1)) # 2026 β birinchi guruh
print(m.group(2)) # 06 β ikkinchi guruh
print(m.group(3)) # 11 β uchinchi guruh
print(m.groups()) # ('2026', '06', '11') β barcha guruhlar tuple
group(0) (yoki shunchaki group()) β butun moslik. group(1), group(2) ... β qavslar tartibida. groups() β barchasini birdan tuple qilib qaytaradi.
Guruhlarni findall bilan ishlatganda, u faqat guruhlarni qaytaradi:
import re
matn = "Aziz: 25, Malika: 30, Bobur: 19"
print(re.findall(r"(\w+): (\d+)", matn))
# [('Aziz', '25'), ('Malika', '30'), ('Bobur', '19')]
# har bir moslik uchun (ism, yosh) jufti
16.11 Nomli guruhlar (?P<ism>...)¶
Guruhlarni raqam (group(1)) bilan emas, nom bilan chaqirish ancha o'qiladigan kod beradi β ayniqsa guruh ko'p bo'lsa. Sintaksis: (?P<ism>...).
import re
matn = "2026-06-11"
m = re.search(r"(?P<yil>\d{4})-(?P<oy>\d{2})-(?P<kun>\d{2})", matn)
print(m.group("yil")) # 2026
print(m.group("oy")) # 06
print(m.group("kun")) # 11
print(m.groupdict()) # {'yil': '2026', 'oy': '06', 'kun': '11'}
m.groupdict() β barcha nomli guruhlarni lug'at (dict) qilib qaytaradi. Bu juda qulay: natijani to'g'ridan-to'g'ri o'zgaruvchilarga yoki strukturaga solish mumkin.
import re
def sanani_ajrat(s: str) -> dict[str, int] | None:
m = re.fullmatch(r"(?P<kun>\d{2})\.(?P<oy>\d{2})\.(?P<yil>\d{4})", s)
if m is None:
return None
return {k: int(v) for k, v in m.groupdict().items()}
print(sanani_ajrat("11.06.2026")) # {'kun': 11, 'oy': 6, 'yil': 2026}
print(sanani_ajrat("xato")) # None
16.12 Tanlov | β "yoki"¶
Vertikal chiziq | β "yoki" degani: bir nechta variantdan birini tanlaydi.
import re
# 'olma' yoki 'anor' yoki 'uzum'
print(re.findall(r"olma|anor|uzum", "olma anor banan uzum"))
# ['olma', 'anor', 'uzum']
Tanlovni guruh bilan birlashtirib, qismga qo'llash mumkin:
import re
# fayl kengaytmasi: jpg, png yoki gif
matn = "rasm.jpg hujjat.pdf logo.png ikon.gif"
print(re.findall(r"\w+\.(jpg|png|gif)", matn))
# ['jpg', 'png', 'gif'] β faqat mos kengaytmalar (qavs guruh bo'lgani uchun)
Yana bir misol β salomlashishning turli shakllarini topish:
import re
matn = "Salom! Assalomu alaykum. Hayrli kun."
print(re.findall(r"Salom|Assalomu|Hayrli", matn))
# ['Salom', 'Assalomu', 'Hayrli']
16.13 Ekranlash \ β maxsus belgini oddiy qilish¶
. + * ? ( ) [ ] { } | ^ $ \ β bular regex'da maxsus ma'noga ega. Agar shu belgilarning o'zini qidirmoqchi bo'lsak, oldiga \ qo'yib ekranlash (escape) kerak.
import re
# . ni ekranlamasdan β har qanday belgini bildiradi (xato)
print(re.findall(r"3.14", "3.14 va 3X14")) # ['3.14', '3X14'] β ikkalasi ham!
# \. bilan β faqat haqiqiy nuqta
print(re.findall(r"3\.14", "3.14 va 3X14")) # ['3.14'] β to'g'ri
Boshqa maxsus belgilarni ekranlash misoli β narx va qavs:
import re
matn = "Narx: $5 (chegirma) va $10"
print(re.findall(r"\$\d+", matn)) # ['$5', '$10'] β \$ haqiqiy dollar belgisi
print(re.findall(r"\(\w+\)", matn)) # ['(chegirma)'] β \( va \) qavslar
Agar ko'p belgini ekranlash kerak bo'lsa, re.escape() foydalaniladi β u matndagi barcha maxsus belgilarni avtomatik ekranlaydi:
import re
foydalanuvchi_kiritdi = "narx (3.5$)"
xavfsiz = re.escape(foydalanuvchi_kiritdi)
print(xavfsiz) # narx\ \(3\.5\$\)
# endi bu naqshni xavfsiz ishlatish mumkin
print(re.search(xavfsiz, "Yangi narx (3.5$) bugun").group()) # narx (3.5$)
16.14 re.sub β almashtirish (topib, o'zgartirish)¶
re.sub(naqsh, almashtiruvchi, matn) β naqshga mos kelgan barcha qismlarni boshqa matnga almashtiradi. Bu "topib-almashtirish" ning juda kuchli varianti.
import re
# Barcha raqamlarni # bilan yashirish
print(re.sub(r"\d", "#", "Karta: 1234 5678")) # Karta: #### ####
# Ortiqcha bo'shliqlarni bittaga keltirish
print(re.sub(r"\s+", " ", "Salom dunyo\t\tbugun")) # Salom dunyo bugun
Almashtiruvchi matnda guruhga murojaat qilish mumkin β \1, \2 yoki nomli guruh \g<ism>. Bu format o'zgartirishda zo'r ishlaydi:
import re
# Sanani YYYY-MM-DD dan DD.MM.YYYY ga aylantirish
matn = "Bugun 2026-06-11"
natija = re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3.\2.\1", matn)
print(natija) # Bugun 11.06.2026
Almashtiruvchi sifatida funksiya ham berish mumkin β har bir moslik shu funksiyaga Match obyekti bo'lib uzatiladi:
import re
# Har bir sonni ikki barobar oshirish
def ikki_barobar(m: re.Match) -> str:
return str(int(m.group()) * 2)
print(re.sub(r"\d+", ikki_barobar, "5 olma, 10 nok")) # 10 olma, 20 nok
re.subnβsubbilan bir xil, lekin(natija, nechta_almashtirildi)tuplini qaytaradi. Nechta o'zgarish bo'lganini bilish kerak bo'lsa foydali.
16.15 re.split β naqsh bo'yicha bo'lish¶
Oddiy str.split() faqat bitta ajratuvchi bilan ishlaydi. re.split() esa naqsh bo'yicha bo'ladi β bir nechta turli ajratuvchini birato'la qo'llash mumkin.
import re
# Vergul, nuqta-vergul yoki bo'shliq bilan ajratilgan
matn = "olma, anor; uzum banan"
print(re.split(r"[,;\s]+", matn)) # ['olma', 'anor', 'uzum', 'banan']
[,;\s]+ naqshi β "bir yoki ko'p vergul, nuqta-vergul yoki bo'sh joy" β ketma-ket ajratuvchilarni ham to'g'ri yutadi. Yana bir misol:
import re
# Matnni gaplarga bo'lish (. ! ? bo'yicha)
matn = "Salom! Qalaysan? Yaxshi. Rahmat."
gaplar = re.split(r"[.!?]\s*", matn)
print([g for g in gaplar if g]) # ['Salom', 'Qalaysan', 'Yaxshi', 'Rahmat']
16.16 Bayroqlar (flags): IGNORECASE, MULTILINE, DOTALL¶
Bayroqlar regex'ning xatti-harakatini o'zgartiradi. Ular funksiyaga flags= argumenti orqali beriladi.
re.IGNORECASE (qisqa: re.I) β katta-kichik harfni e'tiborsiz qoldiradi:
import re
print(re.findall(r"salom", "Salom SALOM salom", flags=re.IGNORECASE))
# ['Salom', 'SALOM', 'salom']
re.MULTILINE (re.M) β ^ va $ ni har bir qatorning boshi/oxiriga moslaydi (butun matnniki emas):
import re
matn = """1-qator
2-qator
3-qator"""
# MULTILINE'siz: faqat butun matnning boshi
print(re.findall(r"^\d", matn)) # ['1']
# MULTILINE bilan: har qatorning boshi
print(re.findall(r"^\d", matn, flags=re.MULTILINE)) # ['1', '2', '3']
re.DOTALL (re.S) β . belgisi yangi qatorni (\n) ham moslaydi (odatda moslamaydi):
import re
matn = "boshi\noxiri"
print(re.findall(r"boshi.oxiri", matn)) # [] β . qatorni o'tmaydi
print(re.findall(r"boshi.oxiri", matn, flags=re.DOTALL)) # ['boshi\noxiri']
Bir nechta bayroqni birlashtirish uchun | (OR) ishlatiladi: flags=re.IGNORECASE | re.MULTILINE.
16.17 re.compile β naqshni qayta ishlatish¶
Agar bitta naqshni ko'p marta ishlatsang (masalan, siklda har qatorni tekshirish), uni har safar qaytadan "tarjima qilish" samarasiz. re.compile() naqshni bir marta tayyor obyektga aylantiradi, keyin uni qayta-qayta ishlatasan.
import re
# Naqshni bir marta tayyorlash
email_naqsh = re.compile(r"[\w.]+@[\w.]+\.\w+")
# Endi obyektning o'z metodlari bor (re. emas, naqsh.)
print(email_naqsh.search("aloqa: aziz@mail.uz").group()) # aziz@mail.uz
print(email_naqsh.findall("a@b.uz va c@d.com")) # ['a@b.uz', 'c@d.com']
Compiled obyektda .search(), .match(), .fullmatch(), .findall(), .finditer(), .sub(), .split() β barcha metodlar mavjud, faqat naqshni qayta yozish shart emas. Bayroqni ham compile vaqtida berish mumkin:
import re
naqsh = re.compile(r"xato", flags=re.IGNORECASE)
print(naqsh.findall("XATO va Xato va xato")) # ['XATO', 'Xato', 'xato']
Qoida: naqshni faqat bir-ikki marta ishlatsang β to'g'ridan-to'g'ri
re.search(...)yetarli. Ko'p marta (siklda, funksiyada) ishlatsang βre.compilebilan tayyorla. Bu tezroq va o'qiladigan.
16.18 Match obyekti to'liq: .group, .start, .end, .span¶
Har bir muvaffaqiyatli moslik β Match obyekti. Uning foydali metodlari:
.group()/.group(0)β butun moslik,.group(n)βn-guruh,.groups()β barcha guruhlar (tuple),.groupdict()β nomli guruhlar (dict),.start(),.end()β moslik boshlanish/tugash indeksi,.span()β(start, end)tuple.
import re
matn = "Mahsulot kodi: AB-1234 tayyor"
m = re.search(r"(?P<harf>[A-Z]{2})-(?P<son>\d{4})", matn)
print(m.group()) # AB-1234 β butun moslik
print(m.group("harf")) # AB β nomli guruh
print(m.group("son")) # 1234
print(m.groups()) # ('AB', '1234')
print(m.groupdict()) # {'harf': 'AB', 'son': '1234'}
print(m.start(), m.end()) # 15 22
print(m.span()) # (15, 22)
print(matn[m.start():m.end()]) # AB-1234 β span orqali kesib olish
Bu metodlar, ayniqsa finditer bilan birga, matndagi mosliklarni aniq joylashuvi bilan qayta ishlashda zarur bo'ladi.
16.19 REAL: email validatsiyasi¶
Endi o'rganganlarni birlashtirib, amaliy validatorlar yozamiz. Email uchun soddalashtirilgan, lekin amalda ishlaydigan naqsh:
import re
EMAIL = re.compile(r"^[\w.+-]+@[\w-]+\.[\w.-]+$")
def email_togrimi(s: str) -> bool:
return EMAIL.fullmatch(s) is not None
for e in ["aziz@mail.uz", "a.b+test@gmail.com", "xato@", "@yoq.uz", "oddiy matn"]:
print(f"{e:20} β {email_togrimi(e)}")
# aziz@mail.uz β True
# a.b+test@gmail.com β True
# xato@ β False
# @yoq.uz β False
# oddiy matn β False
Naqshni qism-qismga ajratamiz:
- ^[\w.+-]+ β boshida bir yoki ko'p so'z belgisi, nuqta, plyus yoki tire (foydalanuvchi nomi),
- @ β majburiy "kuchukcha",
- [\w-]+ β domen nomi,
- \. β haqiqiy nuqta (ekranlangan),
- [\w.-]+$ β domen kengaytmasi (uz, com, co.uk), oxirigacha.
Eslatma: "100% to'g'ri" email regex'i juda murakkab (RFC standarti). Amalda yuqoridagi kabi soddalashtirilgan naqsh aksar holatlarda yetarli. Mukammal tekshiruv kerak bo'lsa,
email-validatorkabi kutubxonadan foydalaniladi.
16.20 REAL: O'zbekiston telefon raqami (+998)¶
O'zbekiston raqami: +998 mamlakat kodi, keyin 2 raqamli operator kodi, keyin 7 raqam. Odamlar uni turlicha yozadi (+998 90 123-45-67, 998901234567). Avval tozalaymiz, keyin tekshiramiz.
import re
def telefon_normalla(s: str) -> str | None:
# Faqat raqamlarni qoldiramiz (bo'shliq, tire, qavs, + ni olib tashlaymiz)
raqamlar = re.sub(r"\D", "", s)
# 998 bilan boshlanishi va jami 12 raqam bo'lishi kerak
if re.fullmatch(r"998\d{9}", raqamlar):
return "+" + raqamlar
return None
for t in ["+998 90 123 45 67", "998901234567", "90-123-45-67", "+1 555 0100"]:
print(f"{t:20} β {telefon_normalla(t)}")
# +998 90 123 45 67 β +998901234567
# 998901234567 β +998901234567
# 90-123-45-67 β None (mamlakat kodi yo'q)
# +1 555 0100 β None (998 emas)
Bu yondashuv kuchli: avval re.sub(r"\D", "", s) bilan barcha ajratuvchilarni tozalaymiz, keyin toza raqamni fullmatch bilan tekshiramiz. Foydalanuvchi qanday yozishidan qat'i nazar ishlaydi.
Operator kodini ajratib olmoqchi bo'lsak, nomli guruh ishlatamiz:
import re
m = re.fullmatch(r"998(?P<operator>\d{2})(?P<raqam>\d{7})", "998901234567")
print(m.group("operator")) # 90
print(m.group("raqam")) # 1234567
16.21 REAL: parol mustahkamligini tekshirish¶
Yaxshi parol qoidasi: kamida 8 belgi, ichida katta harf, kichik harf, raqam va maxsus belgi bo'lsin. Buni bitta ulkan regex bilan yozish qiyin va o'qilmaydi β shuning uchun har shartni alohida kichik regex bilan tekshiramiz:
import re
def parol_tekshir(p: str) -> list[str]:
xatolar = []
if len(p) < 8:
xatolar.append("kamida 8 belgi bo'lishi kerak")
if not re.search(r"[A-Z]", p):
xatolar.append("katta harf bo'lishi kerak")
if not re.search(r"[a-z]", p):
xatolar.append("kichik harf bo'lishi kerak")
if not re.search(r"\d", p):
xatolar.append("raqam bo'lishi kerak")
if not re.search(r"[!@#$%^&*]", p):
xatolar.append("maxsus belgi (!@#$...) bo'lishi kerak")
return xatolar
print(parol_tekshir("Aziz123!")) # [] β kuchli parol
print(parol_tekshir("aziz"))
# ['kamida 8 belgi...', 'katta harf...', 'raqam...', 'maxsus belgi...']
Bu usul β bitta murakkab naqshdan ko'ra ancha tushunarli va foydalanuvchiga aniq nima yetishmayotganini aytadi. Regex'ni qachon bo'lish kerakligiga yaxshi misol.
16.22 REAL: sana va IP manzil validatsiyasi¶
Sana (DD.MM.YYYY) ni ajratib, qism-qismlari mantiqan to'g'ri ekanini ham tekshiramiz:
import re
def sana_ajrat(s: str) -> tuple[int, int, int] | None:
m = re.fullmatch(r"(?P<kun>\d{1,2})\.(?P<oy>\d{1,2})\.(?P<yil>\d{4})", s)
if m is None:
return None
kun, oy, yil = int(m["kun"]), int(m["oy"]), int(m["yil"])
if not (1 <= kun <= 31 and 1 <= oy <= 12):
return None
return kun, oy, yil
print(sana_ajrat("11.06.2026")) # (11, 6, 2026)
print(sana_ajrat("45.13.2026")) # None β format to'g'ri, lekin qiymat noto'g'ri
print(sana_ajrat("2026/06/11")) # None β format noto'g'ri
Diqqat: m["kun"] β m.group("kun") ning qisqa shakli (Match obyektini lug'at kabi indekslash mumkin).
IP manzil (192.168.1.1) β to'rtta 0-255 oraliqdagi son, nuqta bilan ajratilgan:
import re
# Har bir oktet 0-255: 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d
OKTET = r"(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)"
IP = re.compile(rf"^{OKTET}\.{OKTET}\.{OKTET}\.{OKTET}$")
def ip_togrimi(s: str) -> bool:
return IP.fullmatch(s) is not None
print(ip_togrimi("192.168.1.1")) # True
print(ip_togrimi("255.255.255.0")) # True
print(ip_togrimi("256.1.1.1")) # False β 256 > 255
print(ip_togrimi("1.2.3")) # False β 4 ta bo'lak emas
Bu yerda rf"..." β raw va f-string birga (naqsh ichida {OKTET} o'zgaruvchisini qo'yamiz). Murakkab naqshni kichik bo'laklardan qurish β professional usul.
16.23 REAL: log faylini tahlil qilish¶
Eng kuchli real misol β log parsing. Quyidagi formatdagi loglardan sana, daraja (level) va xabarni ajratib olamiz:
import re
log = """2026-06-11 09:15:23 INFO Server ishga tushdi
2026-06-11 09:16:01 ERROR Bazaga ulanib bo'lmadi
2026-06-11 09:16:05 WARNING Xotira kam qoldi
2026-06-11 09:17:30 ERROR Timeout: 30s"""
NAQSH = re.compile(
r"(?P<sana>\d{4}-\d{2}-\d{2}) "
r"(?P<vaqt>\d{2}:\d{2}:\d{2}) "
r"(?P<level>\w+) "
r"(?P<xabar>.+)"
)
# Faqat ERROR qatorlarini ajratamiz
for m in NAQSH.finditer(log):
if m["level"] == "ERROR":
print(f"[{m['vaqt']}] {m['xabar']}")
# [09:16:01] Bazaga ulanib bo'lmadi
# [09:17:30] Timeout: 30s
Naqsh bir nechta string sifatida yozilgan β Python ularni avtomatik birlashtiradi (qulay, har qism alohida o'qiladi). Nomli guruhlar har qatorni strukturaga aylantiradi: shu tarzda minglab qatorli logni soniyalarda tahlil qilish mumkin.
16.24 REAL: matndan hashtag, son va boshqa naqshlar¶
Ijtimoiy tarmoq matnidan hashtag larni ajratish:
import re
post = "Bugun #Python o'rgandim! #dasturlash #code2026 zo'r ekan"
print(re.findall(r"#\w+", post))
# ['#Python', '#dasturlash', '#code2026']
Matndan barcha sonlarni (butun va kasr) topish:
import re
matn = "Narx 1500.50 som, chegirma 10%, jami 1350.45"
print(re.findall(r"\d+\.?\d*", matn)) # ['1500.50', '10', '1350.45']
# \d+ butun qism, \.? ixtiyoriy nuqta, \d* kasr qism
URL havolalarni ajratish:
import re
matn = "Sayt https://ioqil.uz va https://github.com/iOqil yaxshi"
print(re.findall(r"https?://[\w./-]+", matn))
# ['https://ioqil.uz', 'https://github.com/iOqil']
# https? β 's' ixtiyoriy (http yoki https)
Bu uchala misol regex'ning kundalik ishdagi eng tez-tez uchraydigan vazifalarini ko'rsatadi: matndan kerakli naqshni toping va ajratib oling.
16.25 Ushlamaydigan guruh (?:...) va lookahead¶
Ba'zan guruh kerak (| yoki miqdor uchun), lekin uni ushlab qolish (capture) shart emas. (?:...) β ushlamaydigan guruh: guruhlaydi, lekin natijaga qo'shmaydi:
import re
matn = "2024-yil, 2025-yil"
print(re.findall(r"(\d{4})(?:-yil)", matn)) # ['2024', '2025'] β faqat yil
print(re.findall(r"(\d{4})(-yil)", matn)) # [('2024', '-yil'), ('2025', '-yil')] β ortiqcha
Lookahead β "oldinda nima borligini" guruhga olmasdan tekshirish. (?=...) ijobiy, (?!...) salbiy, (?<=...)/(?<!...) lookbehind:
import re
# Oldida "so'm" bo'lgan son ("so'm" natijaga kirmaydi):
print(re.findall(r"\d+(?= so'm)", "Olma 5000 so'm, Non 3000 so'm")) # ['5000', '3000']
# Oldida "$" bo'lgan son (lookbehind):
print(re.findall(r"(?<=\$)\d+", "narx $100 va 200")) # ['100']
π Lookahead/lookbehind naqshga shart qo'yadi, lekin o'sha qismni natijaga olmaydi. Kuchli parol qoidasida qulay: (?=.*\d) β "ichida raqam bo'lsin".
16.26 Orqa havola β takrorlangan qismni topish¶
Naqsh ichida ilgari ushlangan guruhga , `` bilan murojaat qilish β "xuddi shu" qismni qayta talab qiladi:
import re
# Ketma-ket takrorlangan so'zni top:
print(re.findall(r"(\w+)\s+", "salom salom, xayr dunyo dunyo")) # ['salom', 'dunyo']
# Nomli guruhga orqa havola (?P=ism):
print(bool(re.fullmatch(r"(?P<x>\w)(?P=x)", "aa"))) # True (ikki bir xil harf)
16.27 re.VERBOSE β o'qiladigan murakkab naqsh¶
Uzun naqsh tushunarsiz bo'ladi. re.VERBOSE (yoki re.X) naqsh ichida bo'sh joy va izoh yozishga ruxsat beradi:
import re
telefon = re.compile(r"""
\+998 # davlat kodi
\s? # ixtiyoriy bo'sh joy
\d{2} # operator kodi
\s?
\d{7} # raqam
""", re.VERBOSE)
print(bool(telefon.fullmatch("+998 90 1234567"))) # True
π re.VERBOSEda haqiqiy bo'sh joyni \ (ekranlangan) yoki [ ] bilan yozing β oddiy bo'sh joy e'tibordan qoladi.
16.28 Amaliy maslahatlar va keng tarqalgan xatolar¶
Bobni yakunlab, regex bilan ishlashda esda tutadigan asosiy qoidalar:
- Doim
r"..."ishlat β raw string regex naqshlari uchun standart. - Validatsiyada
fullmatchyoki^...$β "ichida bormi" emas, "to'liq to'g'rimi" kerak bo'lganda. - Murakkab naqshni bo'lib tashla β parol kabi ko'p shartli tekshiruvni alohida kichik regex'larga ajrat. O'qiladigan va xatosi oson topiladigan bo'ladi.
re.compileni ko'p ishlatiladigan naqshlar uchun β siklda yoki funksiyada qayta-qayta chaqirilsa.- Nomli guruh
(?P<ism>...)afzal β raqamli guruhdan ko'ra o'qiladigan. - Ochko'z/dangasa farqini eslab tur β HTML/teg ajratganda
*?(dangasa) kerak bo'ladi. - Regex har narsani yechmaydi β to'liq HTML/JSON parsing uchun maxsus kutubxona (
html.parser,json) ishlat, regex emas.
Mashq qilish maslahati: regex101.com β naqshni real vaqtda sinab ko'rish, har bir belgini izohlash uchun ajoyib vosita. Yangi regex yozayotganda undan foydalan.
Regex β boshda murakkab, lekin amaliyot bilan kuchli qurolingga aylanadi. Endi matndagi istalgan naqshni topish, tekshirish va o'zgartirishni bilasan.
βοΈ Masalalar (20 ta)¶
Bu masalalar shu modul (regex) mavzulariga asoslangan.
import reni unutma.
Oson (1β7):
re.searchbilan tekshir: berilgan matnda kamida bitta raqam bormi?True/Falseqaytaruvchiraqam_bormi(matn)funksiyasini yoz.re.findallbilan matndan barcha raqam guruhlarini (\d+) ro'yxat sifatida chiqar. Sinov:"uy 12, xona 305"β['12', '305'].- Matnda
Pythonso'zi (katta-kichik harfga qaramay) bormi?re.IGNORECASEishlatib tekshir. re.subbilan matndagi barcha raqamlarni*belgisiga almashtir. Sinov:"PIN 1234"β"PIN ****".- Berilgan satr faqat harflardan iboratmi?
^[a-zA-Z]+$bilan tekshiruvchi funksiya yoz. re.findallbilan matndan barcha hashtag (#soz) larni ajrat.re.splitbilan vergul yoki nuqta-vergul bo'yicha matnni bo'laklarga ajrat. Sinov:"a,b;c,d"β['a', 'b', 'c', 'd'].
O'rta (8β14):
- Email validatori yoz:
^[\w.+-]+@[\w-]+\.[\w.-]+$naqshi bilanfullmatchqilibTrue/Falseqaytar. - O'zbekiston telefon raqamini tekshir: avval
re.sub(r"\D", "", s)bilan tozalab, keyin998\d{9}ga mosligini tekshir. re.subbilan matndagi ortiqcha bo'shliqlarni (ketma-ket) bitta probelga keltir (\s+β" ").- Matndan barcha email manzillarni
re.findallbilan ro'yxat qilib ajrat. re.findallbilan matndagi barcha sonlarni (butun va kasr, masalan3.14) top.- Berilgan parolda kamida bitta katta harf, bitta raqam va uzunligi β₯8 ekanini tekshir (uchta alohida
re.search). re.subva guruh bilan sananiYYYY-MM-DDdanDD.MM.YYYYga aylantir.
Murakkab (15β20):
- Nomli guruh
(?P<...>)bilanDD.MM.YYYYsanasini ajratib,groupdict()orqali lug'at qaytar. - Log qatoridan (
2026-06-11 09:15 ERROR xabar) nomli guruhlar bilan sana, daraja va xabarni ajrat. - IP manzil validatori yoz: har bir oktet 0-255 oralig'ida bo'lsin (
192.168.1.1βTrue,256.1.1.1βFalse). - Berilgan loglar ro'yxatidan faqat
ERRORdarajali qatorlarni filtrlab chiqar (finditer+ nomli guruh). re.subga funksiya berib, matndagi har bir sonni 10% oshirib qaytar (100β110).- To'liq parol qoidasi: kamida 8 belgi, katta harf, kichik harf, raqam, maxsus belgi (
!@#$%^&*). Yetishmagan shartlar ro'yxatini qaytar.
β Yechimlar¶
Masala 1
Masala 3
Masala 5
Masala 6
Masala 8
Masala 9
Masala 11
Masala 12
Masala 13
Masala 14
Masala 15
Masala 16
Masala 17
Masala 18
import re
log = """2026-06-11 09:15 INFO Boshlandi
2026-06-11 09:16 ERROR Xato 1
2026-06-11 09:17 WARNING Ogohlantirish
2026-06-11 09:18 ERROR Xato 2"""
NAQSH = re.compile(r"(?P<vaqt>\d{2}:\d{2}) (?P<level>\w+) (?P<xabar>.+)")
for m in NAQSH.finditer(log):
if m["level"] == "ERROR":
print(f"[{m['vaqt']}] {m['xabar']}")
# [09:16] Xato 1
# [09:18] Xato 2
Masala 19
Masala 20
import re
def parol_tekshir(p: str) -> list[str]:
xatolar = []
if len(p) < 8:
xatolar.append("kamida 8 belgi")
if not re.search(r"[A-Z]", p):
xatolar.append("katta harf")
if not re.search(r"[a-z]", p):
xatolar.append("kichik harf")
if not re.search(r"\d", p):
xatolar.append("raqam")
if not re.search(r"[!@#$%^&*]", p):
xatolar.append("maxsus belgi")
return xatolar
print(parol_tekshir("Aziz123!")) # []
print(parol_tekshir("aziz"))
# ['kamida 8 belgi', 'katta harf', 'raqam', 'maxsus belgi']
β Ma'lumotlar bazasi va SQL | Boshlovchilar README β | Keyingi: OOP β ilg'or β