IlmHamroh
Python kursi/Standart kutubxona2/16-dars21 daqiqa
Mundarija (22)

15.2-dars: re: guruh va almashtirish

15-QISM — STANDART KUTUBXONA · 2-dars


1. Kirish va motivatsiya

15.1-darsda naqsh mos keladimi degan savolga javob berdik. Amaliyotda esa bu kam: bizga moslikning ichidagi qismlar kerak.

  • Sanadan yil, oy va kunni alohida olish.
  • Jurnal qatoridan sana, vaqt, daraja, modul va xabarni ajratish.
  • 17.09.2026 ni 2026-09-17 ga aylantirish.
  • Matndagi barcha telefon raqamlarini +998 90 *** ** 67 ko'rinishida yashirish.

Bular uchun uchta vosita kerak: guruhlar — naqsh ichidan qism ajratish, re.sub — topilganini almashtirish va re.split — naqsh bo'yicha bo'lish.

Real vaziyat. Jamoa foydalanuvchi matnlaridan shaxsiy ma'lumotni yashirish uchun shunday yozdi:

python
re.sub(r"\d{9}", "*********", matn, re.IGNORECASE)

Kod xatosiz ishladi, testlar o'tdi. Ishlab chiqarishda esa har bir matndagi faqat birinchi ikkita raqam yashirildi, qolganlari jurnallarga ochiq holda tushdi. Sabab: to'rtinchi pozitsion argument — bu flags emas, count, va re.IGNORECASE ning son qiymati 2 ga teng. Bu darsda shunday tuzoqlarni ham ko'ramiz.

Bu darsda:

  • Tartib raqamli guruhlar: group(1), groups()
  • Nomli guruhlar: (?P<nom>...), groupdict()
  • Ushlamaydigan guruh: (?:...) va findall tuzog'i
  • Ixtiyoriy guruhlar va None
  • Qayta havola: \1, (?P=nom)
  • re.sub: \1, \g<nom>, funksiya bilan almashtirish, subn, count
  • Pozitsion count va flags tuzog'i
  • re.split: guruhli ajratgich, maxsplit, bo'sh qismlar
  • Amaliy: jurnal tahlilchisi va shaxsiy ma'lumotni yashirish

2. Nazariya — chuqur tushuntirish

2.1. Guruhlar

Qavs (...) ikki ish qiladi: naqsh qismlarini birlashtiradi (miqdor butun guruhga qo'llanadi) va mos kelgan matnni ushlaydi.

python
m = re.search(r"(\d{4})-(\d{2})-(\d{2})", "sana: 2026-09-17.")
m.group(0)      # '2026-09-17'  — butun moslik
m.group(1)      # '2026'
m.group(2, 3)   # ('09', '17')
m.groups()      # ('2026', '09', '17')
m.span(2)       # (11, 13)
m[1]            # '2026'  — qisqa yozuv
Metod Natija
m.group(0) / m.group() / m[0] Butun moslik
m.group(n) / m[n] n-guruh
m.groups() Hamma guruhlar kortej
m.start(n), m.end(n), m.span(n) Guruh pozitsiyasi
m.lastindex Oxirgi mos kelgan guruh raqami

Guruhlar ochiluvchi qavs tartibida raqamlanadi: ((a)(b)) da 1 — tashqi, 2 — a, 3 — b.

2.2. Nomli guruhlar

python
m = re.search(r"(?P<yil>\d{4})-(?P<oy>\d{2})", "2026-09")
m["yil"]          # '2026'
m.groupdict()     # {'yil': '2026', 'oy': '09'}

Ikkitadan ko'p guruh bo'lsa — nomli guruh ishlating: kod o'qiladi va naqshga yangi guruh qo'shilganda raqamlar siljimaydi.

2.3. Ushlamaydigan guruh va findall tuzog'i

(?:...) — birlashtiradi, lekin ushlamaydi.

findall natijasi guruhlar soniga bog'liq:

Naqshdagi guruhlar findall qaytaradi
0 ta Butun moslik satrlari
1 ta Faqat shu guruh satrlari
2+ ta Kortejlar ro'yxati
python
re.findall(r"(\w+)=(\d+)", "a=1 b=22")     # [('a', '1'), ('b', '22')]
re.findall(r"\w+=(\d+)", "a=1 b=22")       # ['1', '22']      — butun moslik yo'qoldi!
re.findall(r"(?:\w+)=\d+", "a=1 b=22")     # ['a=1', 'b=22']

Guruhni faqat miqdor yoki muqobil uchun ishlatayotgan bo'lsangiz — (?:...).

2.4. Ixtiyoriy guruhlar

Mos kelmagan guruh — None:

python
m = re.search(r"(\+998)?(\d{9})", "901234567")
m.groups()               # (None, '901234567')
m.groups(default="")     # ('', '901234567')
m.groupdict(default="-")

findall da esa mos kelmagan guruh None emas, bo'sh satr '' bo'ladi.

2.5. Qayta havola

Naqsh ichida avval ushlangan matnni takrorlash:

Yozuv Ma'nosi
\1, \2 1-, 2-guruh matni
(?P=nom) Nomli guruh matni
python
re.findall(r"\b(\w+) \1\b", "bu bu xato xato emas")   # ['bu', 'xato'] — takrorlangan so'zlar
re.search(r"(?P<q>['\"]).*?(?P=q)", "x = 'salom'")    # ochilgan qo'shtirnoq bilan yopilgan

2.6. re.sub — almashtirish

python
re.sub(naqsh, almashtirma, matn, count=0, flags=0)

Almashtirma satr ichida:

Yozuv Ma'nosi
\1, \2 Guruh matni
\g<1> Guruh matni (raqamdan keyin raqam kelsa)
\g<nom> Nomli guruh
\g<0> Butun moslik
\n, \t Yangi qator, tab — almashtirma ichida qayta ishlanadi
python
re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3.\2.\1", "2026-09-17")   # '17.09.2026'
re.sub(r"(\d)", r"\g<1>0", "a1b2")                              # 'a10b20'

r"\10" — "1-guruh va 0" emas, 10-guruh. Kerakligi — r"\g<1>0".

Funksiya bilan almashtirish — har moslik uchun funksiya chaqiriladi, u Match oladi va satr qaytaradi:

python
re.sub(r"\d+", lambda m: str(int(m[0]) * 2), "3 olma 12 nok")    # '6 olma 24 nok'

Funksiya qaytargan satrdagi \ qayta ishlanmaydi — tashqi matnni almashtirma sifatida qo'yishning xavfsiz yo'li.

Funksiya Natija
re.sub(...) Yangi satr
re.subn(...) (yangi_satr, almashtirishlar_soni)

2.7. Pozitsion count va flags

python
re.sub(naqsh, almashtirma, matn, re.I)          # ❌ re.I == 2 → count=2, bayroq YO'Q
re.sub(naqsh, almashtirma, matn, flags=re.I)    # ✅
Versiya Pozitsion count/flags
≤ 3.12 Jim ishlaydi
3.13+ DeprecationWarning
Kelajakda Xato bo'ladi

Xuddi shu tuzoq re.split(naqsh, matn, re.I) da ham bor — u yerda uchinchi argument maxsplit.

Qoida: count, maxsplit va flags ni doim nomi bilan yozing.

2.8. re.split

Holat Natija
Oddiy naqsh Ajratgichlar tashlanadi
Guruhli naqsh Ajratgichlar ham natijaga kiradi
Boshida/oxirida ajratgich Bo'sh satr ''
maxsplit=n Ko'pi bilan n marta bo'ladi
python
re.split(r"[,;]\s*", "olma, nok;uzum")         # ['olma', 'nok', 'uzum']
re.split(r"([,;])", "a,b;c")                   # ['a', ',', 'b', ';', 'c']
re.split(r"\s+", "  a b  ")                    # ['', 'a', 'b', '']
str.split() re.split(r"\s+", ...)
Boshi/oxiridagi bo'shliqni tashlaydi Bo'sh satr qoldiradi
Faqat bitta ajratgich (yoki bo'shliqlar) Istalgan naqsh
Tezroq Moslashuvchan

3. Tez ma'lumotnoma

python
m = re.search(r"(?P<yil>[0-9]{4})-(?P<oy>[0-9]{2})", s)
m["yil"], m.groupdict(), m.groups(default="")

re.findall(r"(?:ab)+", s)                          # ushlamaydigan guruh
re.sub(r"([0-9]{2})\.([0-9]{2})\.([0-9]{4})", r"\3-\2-\1", s)
re.sub(naqsh, lambda m: m[0].upper(), s, count=1, flags=re.I)
yangi, soni = re.subn(naqsh, "", s)
re.split(r"\s*([,;])\s*", s, maxsplit=2)

Qoidalar

2+ guruh — nomli guruhlar
faqat birlashtirish — (?:...)
findall natijasi guruhlar soniga bog'liq
guruhdan keyin raqam — \g<1>0
count, maxsplit, flags — doim nomi bilan
tashqi matn almashtirmada — funksiya orqali

4. Batafsil misollar

Misol 1 — Guruhlar

python
r"""Tartib raqamli va nomli guruhlar; groups va groupdict; (?:...); findall tuzog'i; ixtiyoriy guruh; lastindex; qayta havola."""

import re


def main() -> None:
    print("=== 1. Tartib raqamli guruhlar ===")
    m = re.search(r"(\d{4})-(\d{2})-(\d{2})", "Hisobot sanasi: 2026-09-17.")
    assert m is not None
    print(f"  group(0)={m.group(0)!r}  group(1)={m[1]!r}  group(2, 3)={m.group(2, 3)}")
    print(f"  groups()={m.groups()}  span(2)={m.span(2)}  lastindex={m.lastindex}")
    ichma = re.search(r"((\w+)@(\w+))\.uz", "aloqa: aziz@wisar.uz")
    assert ichma is not None
    print(f"  ichma-ich ((a)(b)) tartibi: {ichma.groups()}")

    print("\n=== 2. Nomli guruhlar ===")
    SANA = re.compile(r"(?P<kun>\d{2})\.(?P<oy>\d{2})\.(?P<yil>\d{4})")
    m = SANA.search("Tug'ilgan: 05.03.1998")
    assert m is not None
    print(f"  m['yil']={m['yil']!r}, groupdict()={m.groupdict()}")
    print(f"  groupindex (nom → raqam): {dict(SANA.groupindex)}")

    print("\n=== 3. ⚠️ findall natijasi guruhlar soniga bog'liq ===")
    matn = "host=localhost port=5432 debug=1"
    for naqsh in (r"\w+=\w+", r"\w+=(\w+)", r"(\w+)=(\w+)", r"(?:\w+)=\w+"):
        print(f"  {naqsh:14} → {re.findall(naqsh, matn)}")

    print("\n=== 4. Miqdor guruhga qo'llanganda ===")
    print(f"  (ha)+     → {re.findall(r'(ha)+', 'hahaha hoho ha')}  ← faqat oxirgi takror")
    print(f"  ((?:ha)+) → {re.findall(r'((?:ha)+)', 'hahaha hoho ha')}")

    print("\n=== 5. Ixtiyoriy guruhlar ===")
    TELEFON = re.compile(r"(\+998)?\s?(\d{2})\s?(\d{3})\s?(\d{2})\s?(\d{2})")
    for raqam in ("+998 90 123 45 67", "901234567"):
        m = TELEFON.fullmatch(raqam)
        assert m is not None
        print(f"  {raqam!r:21} groups()={m.groups()}")
        print(f"  {'':21} groups(default='-')[0]={m.groups(default='-')[0]!r}")
    print(f"  findall da None o'rniga: {re.findall(r'(\+998)?(\d{9})', '901234567')}")

    print("\n=== 6. Muqobillar va lastindex ===")
    TOKEN = re.compile(r"(?P<son>\d+)|(?P<soz>[^\W\d_]+)|(?P<belgi>[^\w\s])")
    for m in TOKEN.finditer("narx 25 so'm!"):
        print(f"  {m[0]!r:8} lastgroup={m.lastgroup:6} lastindex={m.lastindex}")

    print("\n=== 7. Qayta havola ===")
    print(f"  takror so'zlar: {re.findall(r'\b(\w+) \1\b', 'bu bu matnda xato xato bor bor')}")
    iqtibos = re.compile(r"(?P<q>['\"])(?P<ichi>.*?)(?P=q)")
    for m in iqtibos.finditer("""a = "o'zbek" b = 'Wisar' c = "yopilmagan'"""):
        print(f"  qo'shtirnoq={m['q']}  ichi={m['ichi']!r}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Tartib raqamli guruhlar ===
  group(0)='2026-09-17'  group(1)='2026'  group(2, 3)=('09', '17')
  groups()=('2026', '09', '17')  span(2)=(21, 23)  lastindex=3
  ichma-ich ((a)(b)) tartibi: ('aziz@wisar', 'aziz', 'wisar')

=== 2. Nomli guruhlar ===
  m['yil']='1998', groupdict()={'kun': '05', 'oy': '03', 'yil': '1998'}
  groupindex (nom → raqam): {'kun': 1, 'oy': 2, 'yil': 3}

=== 3. ⚠️ findall natijasi guruhlar soniga bog'liq ===
  \w+=\w+        → ['host=localhost', 'port=5432', 'debug=1']
  \w+=(\w+)      → ['localhost', '5432', '1']
  (\w+)=(\w+)    → [('host', 'localhost'), ('port', '5432'), ('debug', '1')]
  (?:\w+)=\w+    → ['host=localhost', 'port=5432', 'debug=1']

=== 4. Miqdor guruhga qo'llanganda ===
  (ha)+     → ['ha', 'ha']  ← faqat oxirgi takror
  ((?:ha)+) → ['hahaha', 'ha']

=== 5. Ixtiyoriy guruhlar ===
  '+998 90 123 45 67'   groups()=('+998', '90', '123', '45', '67')
                        groups(default='-')[0]='+998'
  '901234567'           groups()=(None, '90', '123', '45', '67')
                        groups(default='-')[0]='-'
  findall da None o'rniga: [('', '901234567')]

=== 6. Muqobillar va lastindex ===
  'narx'   lastgroup=soz    lastindex=2
  '25'     lastgroup=son    lastindex=1
  'so'     lastgroup=soz    lastindex=2
  "'"      lastgroup=belgi  lastindex=3
  'm'      lastgroup=soz    lastindex=2
  '!'      lastgroup=belgi  lastindex=3

=== 7. Qayta havola ===
  takror so'zlar: ['bu', 'xato', 'bor']
  qo'shtirnoq="  ichi="o'zbek"
  qo'shtirnoq='  ichi='Wisar'

Nima ko'rsatdi: 2.1–2.5-bo'limlar.

Misol 2 — re.sub va subn

python
r"""Almashtirma satrdagi \1 va \g<nom>; \g<1>0; funksiya bilan almashtirish; subn va count; almashtirmadagi escape; pozitsion count tuzog'i."""

import re
import warnings


def main() -> None:
    print("=== 1. Guruhlarni qayta tartiblash ===")
    print(f"  {re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3.\2.\1', 'Sana: 2026-09-17')}")
    print(f"  {re.sub(r'(?P<kun>\d{2})\.(?P<oy>\d{2})\.(?P<yil>\d{4})', r'\g<yil>-\g<oy>-\g<kun>', '17.09.2026')}")
    print(f"  {re.sub(r'\b(\w+) \1\b', r'\1', 'bu bu matnda xato xato bor')}")
    print(f"  \\g<0> — butun moslik: {re.sub(r'\d+', r'[\g<0>]', 'olma 3, nok 12')}")

    print("\n=== 2. ⚠️ \\10 va \\g<1>0 ===")
    print(f"  r'\\g<1>0' → {re.sub(r'(\d)', r'\g<1>0', 'a1b2')}")
    try:
        re.sub(r"(\d)", r"\10", "a1b2")
    except re.error as xato:
        print(f"  r'\\10'    → {type(xato).__name__}: {xato}")

    print("\n=== 3. Funksiya bilan almashtirish ===")
    narxlar = "Kitob 45000 so'm, daftar 8000 so'm"
    print(f"  {re.sub(r'\d+', lambda m: f'{int(m[0]) * 1.12:,.0f}'.replace(',', ' '), narxlar)}")

    def unvon(m: re.Match[str]) -> str:
        soz = m[0]
        return soz if soz.lower() in {"va", "bilan", "uchun"} else soz.capitalize()

    print(f"  {re.sub(r'[^\W\d_]+', unvon, 'python va rust bilan tizim dasturlash')}")

    print("\n=== 4. subn va count ===")
    matn = "a  b   c    d"
    print(f"  subn:     {re.subn(r'\s+', ' ', matn)}")
    print(f"  count=2:  {re.sub(r'\s+', ' ', matn, count=2)!r}")

    print("\n=== 5. ⚠️ Almashtirmadagi \\ belgilari ===")
    yol1 = r"C:\new\table"
    yol2 = r"C:\Users\aziz"
    print(f"  asl yo'l:              {yol1!r}")
    print(f"  satr almashtirma:      {re.sub(r'YOL', yol1, 'papka: YOL')!r}  ← \\n va \\t buzildi")
    try:
        re.sub(r"YOL", yol2, "papka: YOL")
    except re.error as xato:
        print(f"  {yol2!r} bilan: {type(xato).__name__}: {xato}")
    print(f"  funksiya almashtirma:  {re.sub(r'YOL', lambda m: yol1, 'papka: YOL')!r}")
    print(f"  funksiya, ikkinchi yo'l: {re.sub(r'YOL', lambda m: yol2, 'papka: YOL')!r}")
    print("  ⭐ tashqi matnni almashtirmaga qo'yishda — funksiya")

    print("\n=== 6. ⚠️ Pozitsion count tuzog'i ===")
    matn = "Raqamlar: 901234567, 911234567, 931234567"
    with warnings.catch_warnings(record=True) as ogohlantirishlar:
        warnings.simplefilter("always")
        xato_natija = re.sub(r"\d{9}", "*********", matn, re.IGNORECASE)
    print(f"  re.IGNORECASE ning qiymati: {int(re.IGNORECASE)}")
    print(f"  pozitsion: {xato_natija}")
    print(f"  ogohlantirish: {ogohlantirishlar[0].category.__name__}: {ogohlantirishlar[0].message}")
    print(f"  nomli:     {re.sub(r'\d{9}', '*********', matn, flags=re.IGNORECASE)}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Guruhlarni qayta tartiblash ===
  Sana: 17.09.2026
  2026-09-17
  bu matnda xato bor
  \g<0> — butun moslik: olma [3], nok [12]

=== 2. ⚠️ \10 va \g<1>0 ===
  r'\g<1>0' → a10b20
  r'\10'    → PatternError: invalid group reference 10 at position 1

=== 3. Funksiya bilan almashtirish ===
  Kitob 50 400 so'm, daftar 8 960 so'm
  Python va Rust bilan Tizim Dasturlash

=== 4. subn va count ===
  subn:     ('a b c d', 3)
  count=2:  'a b c    d'

=== 5. ⚠️ Almashtirmadagi \ belgilari ===
  asl yo'l:              'C:\\new\\table'
  satr almashtirma:      'papka: C:\new\table'  ← \n va \t buzildi
  'C:\\Users\\aziz' bilan: PatternError: bad escape \U at position 2
  funksiya almashtirma:  'papka: C:\\new\\table'
  funksiya, ikkinchi yo'l: 'papka: C:\\Users\\aziz'
  ⭐ tashqi matnni almashtirmaga qo'yishda — funksiya

=== 6. ⚠️ Pozitsion count tuzog'i ===
  re.IGNORECASE ning qiymati: 2
  pozitsion: Raqamlar: *********, *********, 931234567
  ogohlantirish: DeprecationWarning: 'count' is passed as positional argument
  nomli:     Raqamlar: *********, *********, *********

Nima ko'rsatdi: 2.6, 2.7-bo'limlar.

Misol 3 — re.split

python
r"""Oddiy va guruhli ajratgich; bo'sh qismlar; maxsplit; str.split bilan solishtirish; oddiy tokenizator."""

import re


def main() -> None:
    print("=== 1. Bir nechta ajratgich ===")
    royxat = "olma, nok;uzum ,  gilos|anor"
    print(f"  [,;|]\\s*        → {re.split(r'[,;|]\s*', royxat)}")
    print(f"  \\s*[,;|]\\s*     → {re.split(r'\s*[,;|]\s*', royxat)}")

    print("\n=== 2. Guruhli ajratgich natijada qoladi ===")
    ifoda = "12 + 7*3 - 4 / 2"
    print(f"  \\s*[-+*/]\\s*   → {re.split(r'\s*[-+*/]\s*', ifoda)}")
    print(f"  \\s*([-+*/])\\s* → {re.split(r'\s*([-+*/])\s*', ifoda)}")

    print("\n=== 3. Bo'sh qismlar ===")
    print(f"  re.split(r'\\s+', '  a b  ') → {re.split(r'\s+', '  a b  ')}")
    print(f"  '  a b  '.split()          → {'  a b  '.split()}")
    print(f"  bo'shlarini tashlash: {[q for q in re.split(r'\s+', '  a b  ') if q]}")
    print(f"  bo'sh moslik bilan: {re.split(r'x*', 'axbc')}")

    print("\n=== 4. maxsplit ===")
    qator = "2026-09-17 14:03:22 ERROR baza: ulanish uzildi: timeout"
    sana, vaqt, daraja, xabar = re.split(r"\s+", qator, maxsplit=3)
    print(f"  daraja={daraja!r}, xabar={xabar!r}")
    manba, _, tafsilot = xabar.partition(": ")
    print(f"  manba={manba!r}, tafsilot={tafsilot!r}")

    print("\n=== 5. Tokenizator: split va findall ===")
    kod = "narx=(soni*12)+ 5"
    print(f"  split bilan:   {[t for t in re.split(r'\s*([=()*+])\s*', kod) if t]}")
    print(f"  findall bilan: {re.findall(r'\d+|\w+|[=()*+]', kod)}")
    print("  ⭐ tokenlar uchun findall/finditer ko'pincha aniqroq (15.3-dars)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Bir nechta ajratgich ===
  [,;|]\s*        → ['olma', 'nok', 'uzum ', 'gilos', 'anor']
  \s*[,;|]\s*     → ['olma', 'nok', 'uzum', 'gilos', 'anor']

=== 2. Guruhli ajratgich natijada qoladi ===
  \s*[-+*/]\s*   → ['12', '7', '3', '4', '2']
  \s*([-+*/])\s* → ['12', '+', '7', '*', '3', '-', '4', '/', '2']

=== 3. Bo'sh qismlar ===
  re.split(r'\s+', '  a b  ') → ['', 'a', 'b', '']
  '  a b  '.split()          → ['a', 'b']
  bo'shlarini tashlash: ['a', 'b']
  bo'sh moslik bilan: ['', 'a', '', 'b', 'c', '']

=== 4. maxsplit ===
  daraja='ERROR', xabar='baza: ulanish uzildi: timeout'
  manba='baza', tafsilot='ulanish uzildi: timeout'

=== 5. Tokenizator: split va findall ===
  split bilan:   ['narx', '=', '(', 'soni', '*', '12', ')', '+', '5']
  findall bilan: ['narx', '=', '(', 'soni', '*', '12', ')', '+', '5']
  ⭐ tokenlar uchun findall/finditer ko'pincha aniqroq (15.3-dars)

Nima ko'rsatdi: 2.8-bo'lim.

Misol 4 — Amaliy: jurnal tahlilchisi va shaxsiy ma'lumotni yashirish

Kirishdagi vaziyat: xizmat jurnallari tahlil uchun boshqa jamoaga yuboriladi. Avval har qatorni nomli guruhlar bilan tuzilgan yozuvga aylantiramiz, keyin xabarlardagi email, telefon va karta raqamlarini yashiramiz, oxirida statistika chiqaramiz. Yashirish natijasi tekshiriladi: chiqishda birorta ham to'liq shaxsiy ma'lumot qolmasligi kerak.

python
"""Nomli guruhlar bilan jurnalni tahlil qilish; funksiya bilan yashirish; subn bilan hisoblash; qoldiq tekshiruvi; statistika."""

import re
from collections import Counter

JURNAL = """\
2026-09-17 09:12:03 INFO  [auth] kirish: aziz@wisar.uz, tel +998 90 123 45 67
2026-09-17 09:12:09 WARN  [pay] karta 8600 1234 5678 9012 rad etildi
2026-09-17 09:13:44 ERROR [pay] to'lov xatosi: user=bek.nazarov@mail.uz summa=125000
2026-09-17 09:14:02 INFO  [auth] parol tiklash: +998911234567
buzilgan qator
2026-09-17 09:15:30 ERROR [db] ulanish uzildi (timeout=30s)
"""

QATOR = re.compile(
    r"""
    (?P<sana>\d{4}-\d{2}-\d{2})\s
    (?P<vaqt>\d{2}:\d{2}:\d{2})\s
    (?P<daraja>INFO|WARN|ERROR)\s+
    \[(?P<modul>\w+)\]\s
    (?P<xabar>.*)
    """,
    re.VERBOSE,
)

EMAIL = re.compile(r"(?P<nom>[\w.]+)@(?P<domen>[\w.]+\.[a-z]{2,})")
TELEFON = re.compile(r"\+998\s?(?P<kod>[0-9]{2})\s?[0-9]{3}\s?[0-9]{2}\s?(?P<oxiri>[0-9]{2})")
KARTA = re.compile(r"\b[0-9]{4}(?:\s?[0-9]{4}){2}\s?(?P<oxiri>[0-9]{4})\b")


def email_yashir(m: re.Match[str]) -> str:
    return f"{m['nom'][0]}***@{m['domen']}"


def yashir(xabar: str) -> tuple[str, Counter[str]]:
    hisob: Counter[str] = Counter()
    for nom, naqsh, almashtirma in (
        ("karta", KARTA, r"**** **** **** \g<oxiri>"),
        ("email", EMAIL, email_yashir),
        ("telefon", TELEFON, r"+998 \g<kod> *** ** \g<oxiri>"),
    ):
        xabar, soni = naqsh.subn(almashtirma, xabar)
        hisob[nom] += soni
    return xabar, hisob


def main() -> None:
    yozuvlar, buzilganlar = [], []
    for raqam, qator in enumerate(JURNAL.splitlines(), start=1):
        if (m := QATOR.fullmatch(qator)) is None:
            buzilganlar.append(raqam)
            continue
        yozuvlar.append(m.groupdict())

    print("=== 1. Tahlil ===")
    print(f"  tuzilgan yozuvlar: {len(yozuvlar)}, buzilgan qatorlar: {buzilganlar}")
    birinchi = yozuvlar[0]
    print(f"  birinchi yozuv kalitlari: {list(birinchi)}")
    print(f"  daraja={birinchi['daraja']!r}, modul={birinchi['modul']!r}")

    print("\n=== 2. Yashirish ===")
    jami: Counter[str] = Counter()
    for yozuv in yozuvlar:
        yozuv["xabar"], hisob = yashir(yozuv["xabar"])
        jami += hisob
        print(f"  [{yozuv['modul']:4}] {yozuv['xabar']}")
    print(f"  yashirilganlar: {dict(sorted(jami.items()))}")

    print("\n=== 3. Qoldiq tekshiruvi ===")
    chiqish = "\n".join(y["xabar"] for y in yozuvlar)
    qolgan = {
        "email": EMAIL.findall(chiqish),
        "telefon": re.findall(r"\+998\s?[0-9]{2}\s?[0-9]{3}", chiqish),
        "karta": re.findall(r"\b[0-9]{4}\s?[0-9]{4}\s?[0-9]{4}", chiqish),
    }
    print(f"  to'liq email qolmadi: {qolgan['email'] == []}")
    print(f"  to'liq telefon qolmadi: {qolgan['telefon'] == []}")
    print(f"  to'liq karta qolmadi: {qolgan['karta'] == []}")

    print("\n=== 4. Statistika ===")
    darajalar = Counter(y["daraja"] for y in yozuvlar)
    print(f"  darajalar: {dict(darajalar)}")
    xatolar = [f"{y['vaqt']} {y['modul']}" for y in yozuvlar if y["daraja"] == "ERROR"]
    print(f"  ERROR lar: {xatolar}")
    muddatlar = [int(s) for y in yozuvlar for s in re.findall(r"timeout=(\d+)s", y["xabar"])]
    print(f"  timeout qiymatlari: {muddatlar}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Tahlil ===
  tuzilgan yozuvlar: 5, buzilgan qatorlar: [5]
  birinchi yozuv kalitlari: ['sana', 'vaqt', 'daraja', 'modul', 'xabar']
  daraja='INFO', modul='auth'

=== 2. Yashirish ===
  [auth] kirish: a***@wisar.uz, tel +998 90 *** ** 67
  [pay ] karta **** **** **** 9012 rad etildi
  [pay ] to'lov xatosi: user=b***@mail.uz summa=125000
  [auth] parol tiklash: +998 91 *** ** 67
  [db  ] ulanish uzildi (timeout=30s)
  yashirilganlar: {'email': 2, 'karta': 1, 'telefon': 2}

=== 3. Qoldiq tekshiruvi ===
  to'liq email qolmadi: True
  to'liq telefon qolmadi: True
  to'liq karta qolmadi: True

=== 4. Statistika ===
  darajalar: {'INFO': 2, 'WARN': 1, 'ERROR': 2}
  ERROR lar: ['09:13:44 pay', '09:15:30 db']
  timeout qiymatlari: [30]

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"findall doim butun moslikni qaytaradi" Guruh bo'lsa — guruh(lar)ni
"(ab)+ hamma takrorlarni ushlaydi" Faqat oxirgisini
"Mos kelmagan guruh — bo'sh satr" Match da None, findall da ''
"r"\10" — 1-guruh va 0" 10-guruh; kerakligi — \g<1>0
"re.sub(p, r, s, re.I) — katta-kichik harfsiz" count=2, bayroqsiz
"Almashtirmadagi \n — ikki belgi" Yangi qatorga aylanadi
"re.split ajratgichni doim tashlaydi" Guruhli naqshda saqlaydi
"re.split(r'\s+', s) = s.split()" Chetlarida bo'sh satr qoladi

6. Keng tarqalgan xatolar va yechimlari

1. Pozitsion flags

python
re.sub(p, r, s, re.I)               # ❌ count=2
re.sub(p, r, s, flags=re.I)         # ✅

2. Guruh qo'shilganda findall natijasi o'zgarib qolishi

python
re.findall(r"\d+(kg|g)", s)         # ❌ faqat 'kg'/'g' qaytadi
re.findall(r"\d+(?:kg|g)", s)       # ✅

3. Raqamli guruhlar ko'payib ketganda

python
m.group(7)                          # ❌ naqsh o'zgarsa siljiydi
m["holat"]                          # ✅ nomli guruh

4. None ni tekshirmaslik

python
m["prefiks"].upper()                # ❌ AttributeError
(m["prefiks"] or "").upper()        # ✅

5. Tashqi matn almashtirmada

python
re.sub(p, foydalanuvchi_matni, s)   # ❌ '\' va '\g' ma'no oladi
re.sub(p, lambda m: foydalanuvchi_matni, s)   # ✅

6. \10 noaniqligi

python
re.sub(r"(\d)", r"\10", s)          # ❌
re.sub(r"(\d)", r"\g<1>0", s)       # ✅

7. split dan keyin bo'sh qismlar

python
re.split(r"\s+", " a b ")           # ['', 'a', 'b', '']
[q for q in re.split(r"\s+", s) if q]   # ✅

8. sub ni tsiklda qayta-qayta kompilyatsiya

python
for s in katta_royxat:
    re.sub(r"murakkab...", ...)     # ⚠️ kesh bor, lekin nomli naqsh yaxshiroq
NAQSH = re.compile(r"murakkab...")  # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 15.1-dars (o'tilgan): naqsh tili
  • 15.3-dars: lookahead, tokenizator, ishlash tezligi
  • 15.16-dars: logging — jurnal formatlari
  • 16.3-dars: CSV va matn fayllarini tozalash
  • 28.11-dars: markazlashgan loglar — shaxsiy ma'lumotni yashirish
  • 20.4-dars: route va parametrlar
  • 24-qism: pandas.Series.str.extract — nomli guruhlar ustunga aylanadi

8. Eng yaxshi amaliyotlar

  1. Ikkitadan ko'p guruh — nomli guruhlar.

  2. Faqat birlashtirish uchun — (?:...).

  3. count, maxsplit, flags — doim nomi bilan.

  4. Murakkab almashtirish — funksiya bilan.

  5. Tashqi matnni almashtirmaga funksiya orqali qo'ying.

  6. subn bilan nechta almashtirish bo'lganini tekshiring.

  7. Yashirishdan keyin qoldiq tekshiruvi qiling.

  8. Naqshga nom bering va VERBOSE bilan hujjatlashtiring.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import re
1.  print(re.search(r"(\w+)@(\w+)", "a@b").groups())
2.  print(re.findall(r"(\d)(\w)", "1a 2b"))
3.  print(re.findall(r"\d(\w)", "1a 2b"))
4.  print(re.search(r"(a)?b", "b").group(1))
5.  print(re.sub(r"(\w+) (\w+)", r"\2 \1", "salom dunyo"))
6.  print(re.sub(r"\d", lambda m: str(int(m[0]) + 1), "a1b9"))
7.  print(re.subn(r"o", "0", "olmos"))
8.  print(re.split(r"(-)", "a-b"))
9.  print(re.split(r"-", "-a-"))
10. print(re.findall(r"(ab)+", "ababab"))
11. print(re.search(r"(?P<x>\d+)", "n=42").groupdict())
12. print(re.sub(r"a", "X", "aaa", count=1))
Javoblar
  1. ('a', 'b')
  2. [('1', 'a'), ('2', 'b')]
  3. ['a', 'b']
  4. None
  5. dunyo salom
  6. a2b10
  7. ('0lm0s', 2)
  8. ['a', '-', 'b']
  9. ['', 'a', '']
  10. ['ab'] — faqat oxirgi takror
  11. {'x': '42'}
  12. Xaa

Vazifa 2: Xatolarni tuzating

python
1.  def yashir(matn):
        return re.sub(r"[0-9]{16}", "****", matn, re.MULTILINE)

2.  def ogirliklar(matn):
        return re.findall(r"[0-9]+(kg|g)", matn)       # ['12kg', '500g'] kutilgan

3.  def formatla(matn):
        return re.sub(r"([0-9])", r"\10", matn)        # '1' → '10'

4.  def yolni_qoy(shablon, yol):
        return re.sub(r"\{YOL\}", yol, shablon)        # yol = r"C:\new"

5.  def prefiks(tel):
        m = re.fullmatch(r"(\+998)?([0-9]{9})", tel)
        return m.group(1).strip("+")
Javoblar
python
1.  def yashir(matn):
        return re.sub(r"[0-9]{16}", "****", matn, flags=re.MULTILINE)

2.  def ogirliklar(matn):
        return re.findall(r"[0-9]+(?:kg|g)", matn)

3.  def formatla(matn):
        return re.sub(r"([0-9])", r"\g<1>0", matn)

4.  def yolni_qoy(shablon, yol):
        return re.sub(r"\{YOL\}", lambda m: yol, shablon)

5.  def prefiks(tel):
        m = re.fullmatch(r"(\+998)?([0-9]{9})", tel)
        if m is None:
            return None
        return (m.group(1) or "+998").strip("+")

Vazifa 3: Sana me'yorlagich

sanani_iso(matn) funksiyasini yozing — matndagi barcha sanalarni YYYY-MM-DD ga aylantirsin:

  1. 17.09.2026, 17/09/2026, 2026-09-17, 17 sentabr 2026 shakllari
  2. Oy nomlari o'zbekcha (lotin) — lug'at bilan
  3. Nomli guruhlar va bitta re.sub + funksiya
  4. Noto'g'ri sana (31.02.2026) — o'zgarmasin va alohida ro'yxatga tushsin
  5. 15 ta sinov bilan tekshiring

Vazifa 4: Shablon dvigateli

toldir(shablon, qiymatlar) yozing:

  1. {{ ism }} — qiymat bilan almashtirilsin (bo'shliqlar ixtiyoriy)
  2. {{ narx | pul }} — filtr: 125000 → 125 000 so'm
  3. Topilmagan kalit — KeyError emas, {{ ?ism }} qoldirilsin va ogohlantirish ro'yxatiga tushsin
  4. Qiymat ichidagi \ va {{ xavfsiz qo'yilsin
  5. subn bilan nechta joy to'ldirilganini qaytarsin

Vazifa 5: Markdown → HTML (kichik)

  1. **qalin** → <b>qalin</b>, *kursiv* → <i>kursiv</i>
  2. `kod` ichidagi * o'zgarmasin
  3. [matn](url) → <a href="url">matn</a>
  4. # Sarlavha → <h1> (MULTILINE)
  5. Qaysi holatlarda regex yetmasligini hujjatlashtiring (15.1, Vazifa 7)

Vazifa 6: CSV qatorini ajratish

  1. re.split bilan , bo'yicha bo'ling, lekin qo'shtirnoq ichidagi vergul bo'linmasin
  2. "a,b",c,"d ""e""" → ['a,b', 'c', 'd "e"']
  3. Natijani csv moduli bilan solishtiring (16.3-dars)
  4. Regex qaysi holatda buziladi — misol toping

Vazifa 7: O'ylash

re.sub da almashtirma satr yoki funksiya bo'lishi mumkin. Boshqa tillarda ham shunday: JavaScript'da str.replace(regex, "$1") yoki str.replace(regex, fn), Java'da Matcher.replaceAll(Function). Nega almashtirma satrining o'z "mini-tili" (\1, \g<nom>, $1) bor va bu qanday xavfsizlik muammolariga olib kelishi mumkin?

Javob

Qisqa javob: Almashtirma satr — eng ko'p uchraydigan holat (guruhlarni qayta tartiblash) uchun qisqa yozuv. Lekin u o'z mini-tiliga ega bo'lgani uchun tashqi matnni almashtirma sifatida qo'yish — bu "kod va ma'lumotni aralashtirish" xatosi: SQL injection bilan bir oiladan. Yechim ham bir xil — ma'lumotni kod sifatida talqin qilinmaydigan kanal orqali uzatish (funksiya).

1. Mini-til nima uchun kerak

Vazifa Satr bilan Funksiya bilan
Sanani qayta tartiblash r"\3.\2.\1" lambda m: f"{m[3]}.{m[2]}.{m[1]}"
Butun moslikni o'rash r"[\g<0>]" lambda m: f"[{m[0]}]"

Satr qisqa, o'qiladi va C darajasida bir marta tahlil qilinadi — tez.

2. Tillar bo'yicha

Til Guruh havolasi Butun moslik Funksiya
Python \1, \g<nom> \g<0>
JavaScript $1, $<nom> $&
Java $1, ${nom} $0 (replaceAll(Function))
.NET $1, ${nom} $0 (MatchEvaluator)

3. Xavfsizlik muammolari

Muammo Misol
Kutilmagan talqin Windows yo'li C:\new → \n yangi qatorga aylanadi
Istisno (DoS) Foydalanuvchi \9 yozsa — PatternError, so'rov yiqiladi
Ma'lumot sizishi JavaScript'da $\`` va $'— moslikdan oldingi/keyingi **butun matnni** qo'yadi; foydalanuvchi nomi$'` bo'lsa, boshqa ma'lumot javobga tushadi
Shablon injection Almashtirma keyinroq boshqa shablon dvigatelidan o'tsa

4. Umumiy tamoyil

Bu — kod va ma'lumotni ajratish tamoyili:

Soha Xato To'g'ri
SQL f"... WHERE ism='{ism}'" Parametrlangan so'rov
Shell os.system(f"ls {yol}") subprocess.run(["ls", yol])
HTML Satr birlashtirish Avtomatik ekranlovchi shablon
Regex naqsh re.search(soz, ...) re.escape(soz)
Regex almashtirma re.sub(p, matn, ...) lambda m: matn

5. Xulosa

  1. Almashtirma mini-tili — qulaylik uchun
  2. Tashqi matn bu tilda talqin qilinsa — xato, istisno yoki ma'lumot sizishi
  3. Yechim: ma'lumotni funksiya orqali uzatish — u talqin qilinmaydi
  4. Bu SQL, shell va HTML dagi injection lar bilan bir xil sinf xato

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda naqshdan ma'lumot ajratish va matnni o'zgartirishni o'rgandik.

Eng muhim uch fikr:

  1. Guruhlar — naqsh ichidan ma'lumot olish. (...) ushlaydi, (?:...) faqat birlashtiradi, (?P<nom>...) esa nom beradi va groupdict() bilan to'g'ridan-to'g'ri lug'at qaytaradi. findall natijasi guruhlar soniga bog'liq — bitta guruh qo'shish butun natijani o'zgartiradi. Mos kelmagan guruh Match da None, findall da ''; takrorlangan guruh faqat oxirgi takrorni saqlaydi.

  2. re.sub — almashtirma satr yoki funksiya. Satrda \1, \g<nom>, \g<0> ishlaydi, guruhdan keyin raqam kelsa — \g<1>0. Funksiya har Match uchun chaqiriladi va uning natijasi talqin qilinmaydi — shuning uchun tashqi matnni almashtirmaga faqat funksiya orqali qo'yish kerak. subn almashtirishlar sonini ham qaytaradi.

  3. count, maxsplit, flags — doim nomi bilan. re.sub(p, r, s, re.I) da re.I bayroq emas, count=2 bo'lib qoladi — jim, xatosiz, faqat 3.13+ da ogohlantirish bilan. re.split esa guruhli naqshda ajratgichlarni natijada saqlaydi va chetlarda bo'sh satrlar qoldiradi.

Keyingi darsda amaliy naqshlarga o'tamiz: lookahead va lookbehind, tokenizator yozish, katastrofik backtracking va regexni qachon ishlatmaslik kerak.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!