Tarkibga o'tish

1.11 Muntazam ifodalar (regex)

⬅️ Oldingi: 1.10 Anonim funksiyalar va massiv vositalari (map / filter / reduce) Β· 🏠 README Β· Keyingi: 1.12 Sana va vaqt ➑️


Tasavvur qiling: ro'yxatdan o'tish formangiz bor va foydalanuvchi email kiritdi. Bu haqiqiy emailmi? Telefon raqami +998 bilan boshlanyaptimi va to'g'ri uzunlikdami? Parol yetarlicha kuchlimi? Katta matn ichidan barcha hashtag'larni ajratib olish kerakmi? Bularning hammasini oddiy str_contains yoki substr bilan qilish β€” azob: har bir holatni qo'lda tekshirish kerak bo'ladi.

Mana shu yerda regex (regular expression β€” muntazam ifoda) yordamga keladi. Regex β€” bu matn naqshini (qolipini) tasvirlaydigan maxsus til. Siz "menga shunaqa ko'rinishdagi matn kerak" deysiz, regex esa matn shu naqshga mos keladimi-yo'qmi aytadi yoki mos kelgan qismlarini topib beradi.

Analogiya: regex β€” bu "qidiruvning superkuchli versiyasi". Oddiy qidiruvda aniq so'zni qidirasiz ("olma"). Regex'da esa qoidani qidirasiz: "uchta raqam, keyin chiziqcha, keyin yana ikkita raqam". Bitta naqsh minglab turli matnlarga mos kelishi mumkin.

PHP'da regex bilan ishlash uchun PCRE (Perl Compatible Regular Expressions β€” Perl tiliga mos muntazam ifodalar) deb ataladigan funksiyalar to'plami bor. Ularning nomi preg_ bilan boshlanadi: preg_match, preg_match_all, preg_replace, preg_split. Shu bobda ularni chuqur o'rganamiz.

Ogohlantirish: regex kuchli, lekin har doim ham kerak emas. Email yoki URL tekshirish uchun PHP'da tayyor filter_var vositasi bor (bob oxirida ko'ramiz) β€” u regex'dan ko'ra ishonchliroq. Regex'ni o'z naqshlaringiz uchun (telefon formati, mahsulot kodi, sana) ishlating.

Birinchi tekshiruv β€” preg_match

preg_match β€” eng asosiy funksiya. U "matn ushbu naqshga mos keladimi?" degan savolga javob beradi. Mos kelsa 1, kelmasa 0 qaytaradi (xatolik bo'lsa false).

<?php
$matn = "Salom dunyo";

// Matn ichida "dunyo" so'zi bormi?
$natija = preg_match('/dunyo/', $matn);

var_dump($natija);   // int(1)  β€” topildi

Birinchi argument β€” naqsh (pattern), ikkinchisi β€” tekshiriladigan matn. Naqsh ikki tomonidan / belgisi bilan o'ralgan β€” bu delimiter (ajratgich, naqshning chegarasi). Delimiter haqida birozdan keyin batafsil gaplashamiz.

Odatda preg_match ni if ichida ishlatamiz, chunki 1 β€” true, 0 β€” false deb qabul qilinadi:

<?php
$matn = "Salom dunyo";

if (preg_match('/dunyo/', $matn)) {
    echo "Topildi!";
} else {
    echo "Topilmadi.";
}
// Natija: Topildi!

Muhim farq: str_contains($matn, "dunyo") ham xuddi shu ishni qiladi va tezroq. Oddiy so'zni qidirayotgan bo'lsangiz, str_contains ishlating. Regex'ning kuchi β€” naqsh (raqamlar, harflar, formatlar) qidirishda namoyon bo'ladi.

Naqsh chegarasi β€” delimiter /.../

Har bir regex naqshi ikki tomonidan bir xil belgi bilan o'ralishi shart. Eng ko'p ishlatiladigani β€” /:

<?php
preg_match('/abc/', $matn);   // /.../ β€” eng keng tarqalgan delimiter

Lekin agar naqsh ichida / belgisi ko'p bo'lsa (masalan, sana 2026/06/11 yoki URL), uni har safar ekranlash (\/) noqulay. Shunda boshqa delimiter tanlash mumkin β€” masalan # yoki ~:

<?php
$sana = "2026/06/11";

// / o'rniga # delimiter ishlatamiz β€” endi naqsh ichidagi / ni ekranlash shart emas
if (preg_match('#\d{4}/\d{2}/\d{2}#', $sana)) {
    echo "Sana formati to'g'ri";
}
// Natija: Sana formati to'g'ri

Delimiter sifatida harf, raqam yoki bo'sh joydan tashqari deyarli har qanday belgi ishlatish mumkin (/, #, ~, !, @). Kitobning qolgan qismida asosan / ishlatamiz, lekin / ko'p bo'lganda # ga o'tishni eslab qoling.

Bog'lagichlar (anchor) β€” ^ va $

Hozircha naqshlarimiz matnning istalgan qismida moslik qidirardi. Ko'pincha esa biz "matn to'liq shu naqshga mos kelsinmi?" deb tekshirmoqchi bo'lamiz. Buning uchun ikki anchor (bog'lagich) bor:

  • ^ β€” matnning boshi
  • $ β€” matnning oxiri
<?php
// "salom" SO'ZI bormi (istalgan joyda)?
var_dump(preg_match('/salom/', "men salom dedim"));   // int(1)

// Matn AYNAN "salom" dan boshlanyaptimi?
var_dump(preg_match('/^salom/', "salom dunyo"));      // int(1)
var_dump(preg_match('/^salom/', "men salom dedim"));  // int(0) β€” boshida emas

// Matn AYNAN "salom" bilan tugayaptimi?
var_dump(preg_match('/salom$/', "men salom"));        // int(1)

Eng muhimi β€” ^ va $ ni birga ishlatib, butun matnni tekshirish. Bu validatsiyada doimo kerak bo'ladi:

<?php
// Matn FAQAT "ha" yoki boshqa hech narsa bo'lmagan holdami?
var_dump(preg_match('/^ha$/', "ha"));          // int(1) β€” aynan "ha"
var_dump(preg_match('/^ha$/', "ha mayli"));    // int(0) β€” ortiqcha matn bor

Qoida: validatsiyada (email, telefon to'g'rimi) deyarli har doim ^...$ ishlating. Aks holda foydalanuvchi ali@mail.com BLA BLA deb yozsa ham "to'g'ri" deb o'tib ketadi.

Belgi sinflari β€” [...]

[...] β€” belgi sinfi (character class). U "shu belgilardan bittasi" degani. Masalan, [abc] β€” a yoki b yoki c.

<?php
// "kat" yoki "kot" yoki "kit" β€” ikkinchi harf a, o yoki i bo'lsin
var_dump(preg_match('/^k[aoi]t$/', "kot"));   // int(1)
var_dump(preg_match('/^k[aoi]t$/', "ket"));   // int(0) β€” e yo'q ro'yxatda

Belgilar oralig'ini - bilan beramiz β€” bu juda foydali:

<?php
// [a-z] β€” kichik lotin harflari
// [A-Z] β€” katta lotin harflari
// [0-9] β€” raqamlar
var_dump(preg_match('/^[a-z]$/', "g"));   // int(1)
var_dump(preg_match('/^[0-9]$/', "7"));   // int(1)
var_dump(preg_match('/^[0-9]$/', "x"));   // int(0)

Oraliqlarni birlashtirish ham mumkin: [a-zA-Z0-9] β€” istalgan harf yoki raqam.

Sinfni inkor qilish β€” [^...]

Belgi sinfi ichida birinchi belgi ^ bo'lsa, ma'no teskariga aylanadi: "shu belgilardan tashqari istalgan biri".

<?php
// [^0-9] β€” raqam BO'LMAGAN istalgan belgi
var_dump(preg_match('/[^0-9]/', "12345"));   // int(0) β€” hammasi raqam, raqammas belgi yo'q
var_dump(preg_match('/[^0-9]/', "123a45"));  // int(1) β€” 'a' raqammas, topildi

Diqqat: ^ belgisi ikki xil ma'noga ega! Sinf ichida birinchi o'rinda ([^...]) β€” inkor. Sinfdan tashqarida (^salom) β€” matn boshi. Joylashuviga qarab farqlang.

Tayyor qisqartmalar β€” \d \w \s

Eng ko'p ishlatiladigan belgi sinflari uchun qisqa yozuvlar mavjud. Ularni eslab qolish kerak:

Qisqartma Ma'nosi Teng
\d raqam (digit) [0-9]
\w so'z belgisi (word): harf, raqam, pastki chiziq [a-zA-Z0-9_]
\s bo'sh joy (space): probel, tab, yangi qator [ \t\n\r]

Ularning katta harfli versiyasi β€” teskari ma'no:

Qisqartma Ma'nosi
\D raqam BO'LMAGAN
\W so'z belgisi BO'LMAGAN
\S bo'sh joy BO'LMAGAN
<?php
var_dump(preg_match('/^\d$/', "5"));    // int(1) β€” bitta raqam
var_dump(preg_match('/^\w$/', "_"));    // int(1) β€” pastki chiziq so'z belgisi
var_dump(preg_match('/^\s$/', " "));    // int(1) β€” bitta probel
var_dump(preg_match('/^\D$/', "a"));    // int(1) β€” raqammas belgi

Miqdor belgilari β€” * + ? {n} {n,m}

Hozirgacha bitta belgi bittaga mos kelardi. Miqdor belgilari (quantifier) o'zidan oldingi element necha marta takrorlanishini bildiradi:

Belgi Ma'nosi
* 0 yoki undan ko'p marta
+ 1 yoki undan ko'p marta (kamida bitta)
? 0 yoki 1 marta (ixtiyoriy)
{n} aynan n marta
{n,m} n dan m gacha marta
{n,} kamida n marta
<?php
// \d+ β€” bir yoki undan ko'p raqam (kamida bitta)
var_dump(preg_match('/^\d+$/', "12345"));   // int(1) β€” faqat raqamlardan iborat
var_dump(preg_match('/^\d+$/', "123a"));    // int(0) β€” 'a' bor
var_dump(preg_match('/^\d+$/', ""));        // int(0) β€” bo'sh, kamida bitta kerak

// \d{4} β€” aynan 4 ta raqam (masalan, yil)
var_dump(preg_match('/^\d{4}$/', "2026"));  // int(1)
var_dump(preg_match('/^\d{4}$/', "202"));   // int(0) β€” 3 ta

// \d{2,4} β€” 2 dan 4 gacha raqam
var_dump(preg_match('/^\d{2,4}$/', "999")); // int(1) β€” 3 ta, oraliqda

? β€” biror narsani ixtiyoriy qilish uchun juda foydali:

<?php
// "colour" va "color" β€” ikkalasi ham mos (u bo'lishi ham, bo'lmasligi ham mumkin)
var_dump(preg_match('/^colou?r$/', "color"));   // int(1)
var_dump(preg_match('/^colou?r$/', "colour"));  // int(1)

Guruhlash β€” (...)

Qavslar (...) bir nechta belgini bitta guruhga birlashtiradi. Endi miqdor belgisini butun guruhga qo'llash mumkin:

<?php
// (ha)+ β€” "ha" so'zi bir yoki ko'p marta: "ha", "haha", "hahaha"...
var_dump(preg_match('/^(ha)+$/', "hahaha"));   // int(1)
var_dump(preg_match('/^(ha)+$/', "haa"));      // int(0) β€” "ha" + "a", guruh buzildi

// (\d{3}-)+ β€” "123-456-789-" kabi takrorlanuvchi bloklar
var_dump(preg_match('/^(\d{3}-)+\d{3}$/', "111-222-333"));   // int(1)

Guruhlar yana capture group (ushlab qolish guruhi) vazifasini bajaradi β€” naqshning mos kelgan qismini ajratib olishga imkon beradi. Buni keyinroq batafsil ko'ramiz.

Tanlov β€” |

| belgisi "yoki" degani. kun|tun β€” "kun" yoki "tun".

<?php
// "ha" yoki "yoq" yoki "mayli"
$naqsh = '/^(ha|yoq|mayli)$/';

var_dump(preg_match($naqsh, "ha"));     // int(1)
var_dump(preg_match($naqsh, "yoq"));    // int(1)
var_dump(preg_match($naqsh, "balki"));  // int(0)

Tanlovni guruh ichiga olish odatda zarur β€” aks holda | butun naqshga taalluqli bo'lib qoladi. ^ha|yoq$ β€” "ha bilan boshlanadi YOKI yoq bilan tugaydi" degan ma'noni beradi (xato!), ^(ha|yoq)$ esa β€” "aynan ha yoki aynan yoq".

Maxsus belgilarni ekranlash β€” \

. * + ? ( ) [ ] { } ^ $ | \ / β€” bular regex'da maxsus ma'noga ega. Agar ularning o'zini (oddiy belgi sifatida) qidirmoqchi bo'lsangiz, oldiga \ (teskari chiziq) qo'yib ekranlash (escape) kerak.

Eng ko'p uchraydigan misol β€” nuqta .. Regex'da . "istalgan bitta belgi" degani:

<?php
// . ekranlanmagan β€” "istalgan belgi" degani
var_dump(preg_match('/^a.c$/', "axc"));   // int(1) β€” x ham mos
var_dump(preg_match('/^a.c$/', "a c"));   // int(1) β€” probel ham mos

// \. ekranlangan β€” endi AYNAN nuqta belgisi
var_dump(preg_match('/^a\.c$/', "a.c"));  // int(1)
var_dump(preg_match('/^a\.c$/', "axc"));  // int(0) β€” x nuqta emas

Bu, masalan, fayl kengaytmasini yoki email'dagi nuqtani tekshirishda muhim:

<?php
// Fayl .txt bilan tugayaptimi? Nuqtani ekranlaymiz
var_dump(preg_match('/\.txt$/', "hisobot.txt"));   // int(1)
var_dump(preg_match('/\.txt$/', "hisobotXtxt"));   // int(0) β€” nuqta yo'q

Bayroqlar β€” i (registr) va m (ko'p qator)

Naqshning yopuvchi delimiter'idan keyin harflar qo'shib, uning xulqini o'zgartirish mumkin. Bularni bayroq (flag, modifier) deyiladi.

i bayrog'i β€” registr (katta/kichik harf) farqini e'tiborsiz qoldiradi:

<?php
// i bayrog'isiz β€” registr muhim
var_dump(preg_match('/salom/', "SALOM"));    // int(0)

// i bayrog'i bilan β€” SALOM, Salom, salom β€” barchasi mos
var_dump(preg_match('/salom/i', "SALOM"));   // int(1)
var_dump(preg_match('/salom/i', "Salom"));   // int(1)

m bayrog'i β€” ko'p qatorli rejim. Bunda ^ va $ butun matnning emas, har bir qatorning boshi va oxiriga mos keladi:

<?php
$matn = "birinchi\nikkinchi\nuchinchi";

// m'siz: ^\w+ butun matn boshidan faqat 1 ta moslik
preg_match_all('/^\w+/', $matn, $m1);
var_dump($m1[0]);   // array(1) { [0]=> "birinchi" }

// m bilan: har bir qator boshidan moslik
preg_match_all('/^\w+/m', $matn, $m2);
var_dump($m2[0]);   // array(3) { "birinchi", "ikkinchi", "uchinchi" }

Bayroqlarni birlashtirish mumkin: /naqsh/im β€” ham registrga befarq, ham ko'p qatorli.

Mosliklarni ushlab olish β€” capture group

preg_match ning uchinchi argumenti β€” o'zgaruvchi. Unga moslik natijalari massiv bo'lib joylanadi. &$matches orqali (havola bilan) PHP shu o'zgaruvchini to'ldiradi:

<?php
$matn = "Buyurtma raqami: 12345";

preg_match('/\d+/', $matn, $natija);

var_dump($natija);
// array(1) { [0]=> string(5) "12345" }

echo $natija[0];   // 12345 β€” topilgan butun moslik

Endi eng kuchli imkoniyat β€” qavs (...) orqali naqshning alohida qismlarini ajratib olish. $matches[0] β€” butun moslik, $matches[1] β€” birinchi qavs, $matches[2] β€” ikkinchi qavs:

<?php
$sana = "2026-06-11";

// Uchta guruh: yil, oy, kun
preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $sana, $m);

echo "Yil: {$m[1]}\n";   // Yil: 2026
echo "Oy:  {$m[2]}\n";   // Oy:  06
echo "Kun: {$m[3]}\n";   // Kun: 11
// $m[0] esa butun moslik: "2026-06-11"

Bu β€” regex'ning eng amaliy qismi: matndan kerakli ma'lumotni bo'laklab olish.

Hammasini topish β€” preg_match_all

preg_match faqat birinchi moslikni topadi. Agar matndan barcha mosliklarni topish kerak bo'lsa β€” preg_match_all ishlating. U ham xuddi shunday ishlaydi, lekin natija massivga barcha mosliklarni yig'adi:

<?php
$matn = "Narxlar: 100, 250 va 999 so'm";

preg_match_all('/\d+/', $matn, $natija);

var_dump($natija[0]);
// array(3) { [0]=> "100", [1]=> "250", [2]=> "999" }

// Endi ular ustida ishlash mumkin β€” masalan yig'indi
$jami = array_sum($natija[0]);
echo "Jami: {$jami}";   // Jami: 1349

Capture group bilan birga ishlatganda yanada kuchli. Masalan, matndan barcha hashtag'larni ajratib olish:

<?php
$post = "Bugun #php va #dasturlash o'rganyapman. #kod hayot!";

preg_match_all('/#(\w+)/', $post, $m);

var_dump($m[0]);   // ["#php", "#dasturlash", "#kod"]  β€” # bilan
var_dump($m[1]);   // ["php", "dasturlash", "kod"]     β€” #'siz (1-guruh)

$m[0] β€” to'liq mosliklar, $m[1] β€” har bir moslikdagi birinchi guruh qiymatlari.

Almashtirish β€” preg_replace

preg_replace naqshga mos kelgan barcha qismlarni boshqasiga almashtiradi. str_replace ga o'xshaydi, lekin aniq so'z emas, naqsh bo'yicha ishlaydi:

<?php
$matn = "Mening raqamim 901234567";

// Barcha raqamlarni * bilan yashirish (raqam maxfiyligi)
$yashirin = preg_replace('/\d/', '*', $matn);
echo $yashirin;   // Mening raqamim *********

Matnni tozalashda juda qo'l keladi. Masalan, ortiqcha bo'sh joylarni bittaga keltirish:

<?php
$xom = "Ali     Valiyev      keldi";

// Bir yoki ko'p bo'sh joyni bitta probelga almashtirish
$toza = preg_replace('/\s+/', ' ', $xom);
echo $toza;   // Ali Valiyev keldi

Yoki faqat raqamlarni qoldirish (telefon raqamini tozalash):

<?php
$kiritilgan = "+998 (90) 123-45-67";

// Raqam BO'LMAGAN hamma narsani o'chirish (bo'sh matnga almashtirish)
$faqat_raqam = preg_replace('/\D/', '', $kiritilgan);
echo $faqat_raqam;   // 998901234567

Almashtirishda guruhni ishlatish β€” preg_replace'ning yana bir kuchi. Almashtirish matnida $1, $2 orqali ushlab qolingan guruhlarga murojaat qilish mumkin. Masalan, sana formatini o'zgartirish:

<?php
$sana = "2026-06-11";

// YYYY-MM-DD ni DD.MM.YYYY ga aylantirish
$yangi = preg_replace('/(\d{4})-(\d{2})-(\d{2})/', '$3.$2.$1', $sana);
echo $yangi;   // 11.06.2026

Ajratish β€” preg_split

preg_split β€” explodening regex versiyasi. Matnni naqsh bo'yicha bo'laklarga ajratadi. explode faqat aniq belgi bilan ajratsa, preg_split murakkab ajratgichlar bilan ishlaydi:

<?php
$matn = "olma, anor;uzum,  banan";

// Ajratgich: vergul YOKI nuqta-vergul, atrofida bo'sh joy bo'lishi ham mumkin
$mevalar = preg_split('/\s*[,;]\s*/', $matn);

var_dump($mevalar);
// array(4) { "olma", "anor", "uzum", "banan" }

explode(",", ...) bu ishni eplay olmasdi, chunki ajratgich har xil (, va ;) va atrofida turli bo'sh joylar bor. Yana bir misol β€” gapni so'zlarga ajratish:

<?php
$gap = "Bir   ikki\tuch\nto'rt";

// Istalgan bo'sh joy (probel, tab, yangi qator) bo'yicha ajratish
$sozlar = preg_split('/\s+/', $gap);

var_dump($sozlar);
// array(4) { "Bir", "ikki", "uch", "to'rt" }

Real misol β€” email validatsiya

Endi o'rganganlarimizni real masalalarga qo'llaymiz. Eng mashhuri β€” email tekshirish. Soddalashtirilgan naqsh:

<?php
function emailToghrimi(string $email): bool
{
    // ^...$ β€” butun matn tekshiriladi
    // [\w.+-]+  β€” foydalanuvchi nomi: harf, raqam, _, ., +, -
    // @         β€” majburiy @ belgisi
    // [\w-]+    β€” domen nomi
    // (\.[\w-]+)+ β€” kamida bitta ".uz" yoki ".com" qismi
    $naqsh = '/^[\w.+-]+@[\w-]+(\.[\w-]+)+$/';

    return preg_match($naqsh, $email) === 1;
}

var_dump(emailToghrimi("ali@mail.com"));        // bool(true)
var_dump(emailToghrimi("oqil.dev@gmail.uz"));   // bool(true)
var_dump(emailToghrimi("xato@mail"));           // bool(false) β€” nuqta yo'q
var_dump(emailToghrimi("@mail.com"));           // bool(false) β€” nom yo'q
var_dump(emailToghrimi("ali mail.com"));        // bool(false) β€” @ yo'q

Eslatma: real loyihada email uchun regex emas, filter_var ishlating (bob oxirida). Bu yerda regex'ni o'rganish maqsadida ko'rsatdik. Lekin telefon yoki o'z formatlaringiz uchun regex aynan kerak bo'ladi.

Real misol β€” O'zbekiston telefon raqami

O'zbekiston mobil raqami +998 bilan boshlanib, jami 9 ta raqamdan iborat bo'ladi: +998901234567. Foydalanuvchilar uni turlicha yozadi β€” bo'sh joy, qavs, chiziqcha bilan. Avval tozalab, keyin tekshiramiz:

<?php
function telefonToghrimi(string $raqam): bool
{
    // 1-qadam: raqam va + dan boshqa hamma narsani olib tashlash
    $toza = preg_replace('/[^\d+]/', '', $raqam);

    // 2-qadam: +998 dan keyin aynan 9 ta raqam bormi?
    return preg_match('/^\+998\d{9}$/', $toza) === 1;
}

var_dump(telefonToghrimi("+998901234567"));        // bool(true)
var_dump(telefonToghrimi("+998 90 123 45 67"));    // bool(true) β€” tozalandi
var_dump(telefonToghrimi("+998 (90) 123-45-67"));  // bool(true) β€” tozalandi
var_dump(telefonToghrimi("998901234567"));         // bool(false) β€” + yo'q
var_dump(telefonToghrimi("+99890123456"));         // bool(false) β€” raqam kam

Bu yerda ikki regex'ni birga ishlatdik: avval preg_replace bilan tozaladik, keyin preg_match bilan tekshirdik. Bu β€” amalda juda keng tarqalgan usul.

Real misol β€” kuchli parol tekshiruvi

"Kuchli parol" odatda bir nechta shartga javob berishi kerak: kamida 8 belgi, katta harf, kichik harf, raqam. Buni bitta murakkab regex bilan ham qilish mumkin (lookahead orqali), lekin o'qishliroq yo'l β€” har shartni alohida tekshirish:

<?php
function parolKuchlimi(string $parol): bool
{
    // Har bir shart β€” alohida kichik regex
    $uzunlik = strlen($parol) >= 8;
    $kattaHarf = preg_match('/[A-Z]/', $parol) === 1;
    $kichikHarf = preg_match('/[a-z]/', $parol) === 1;
    $raqam = preg_match('/\d/', $parol) === 1;

    // Hammasi true bo'lsagina parol kuchli
    return $uzunlik && $kattaHarf && $kichikHarf && $raqam;
}

var_dump(parolKuchlimi("Parol123"));    // bool(true) β€” barcha shart bajarildi
var_dump(parolKuchlimi("parol123"));    // bool(false) β€” katta harf yo'q
var_dump(parolKuchlimi("Parol"));       // bool(false) β€” raqam yo'q va qisqa
var_dump(parolKuchlimi("PAROL123"));    // bool(false) β€” kichik harf yo'q

Maslahat: bitta ulkan regex yozishdan ko'ra, bir nechta sodda regex'ni mantiqiy && bilan birlashtirish β€” ko'pincha to'g'riroq tanlov. Kod o'qishli bo'ladi va xatoni topish oson.

Foydali alternativa β€” filter_var

Regex'ni yakunlashdan oldin muhim narsani aytib o'tamiz. Ba'zi keng tarqalgan tekshiruvlar uchun PHP'da tayyor, sinovdan o'tgan vosita bor β€” filter_var. U email, URL, IP, butun son kabilarni regex'siz tekshiradi:

<?php
// Email β€” regex emas, filter_var
$email = "ali@mail.com";
if (filter_var($email, FILTER_VALIDATE_EMAIL) !== false) {
    echo "Email to'g'ri\n";
}

// URL
$url = "https://ioqil.uz";
if (filter_var($url, FILTER_VALIDATE_URL) !== false) {
    echo "URL to'g'ri\n";
}

// Butun son
var_dump(filter_var("42", FILTER_VALIDATE_INT));   // int(42)
var_dump(filter_var("abc", FILTER_VALIDATE_INT));  // bool(false)

Qoida: agar tekshiruvingiz uchun tayyor FILTER_VALIDATE_* bo'lsa β€” uni ishlating, u ishonchliroq. Regex'ni esa o'zingizning maxsus formatlaringiz uchun (telefon, mahsulot kodi, sana shabloni, hashtag ajratish) qoldiring. Ko'p hollarda eng yaxshi yechim β€” ikkalasini birga ishlatish.

Qisqacha xulosa

  • preg_match($naqsh, $matn, &$m) β€” birinchi moslik bormi (1/0); $m ga ushlab oladi.
  • preg_match_all(...) β€” barcha mosliklar.
  • preg_replace($naqsh, $almash, $matn) β€” naqsh bo'yicha almashtirish ($1 orqali guruhlar).
  • preg_split($naqsh, $matn) β€” naqsh bo'yicha bo'laklarga ajratish.
  • Naqsh qismlari: delimiter /.../, anchor ^ $, sinf [a-z] [^...], qisqartma \d \w \s, miqdor * + ? {n} {n,m}, guruh (...), tanlov |, ekranlash \., bayroq i m.
  • Murakkab tekshiruvni bitta ulkan regex'ga tiqishtirmang β€” sodda bo'laklarga bo'ling.
  • Tayyor narsa bo'lsa (filter_var) β€” regex o'rniga uni ishlating.

Mashqlar

Oson 1. preg_match bilan tekshiring: "abc123" matnida raqam bormi (/\d/)? 2. "Salom" matni katta harf bilan boshlanyaptimi tekshiring (/^[A-Z]/). 3. preg_match bilan tekshiring: matn faqat raqamlardan iboratmi (/^\d+$/). "12345" va "12a45" ga sinab ko'ring. 4. preg_match bilan "fayl.txt" .txt bilan tugaganini tekshiring (nuqtani ekranlashni unutmang). 5. preg_replace bilan "a1b2c3" matnidagi barcha raqamlarni # belgisiga almashtiring.

O'rta 6. emailToghrimi funksiyasini yozing: email ^[\w.+-]+@[\w-]+\.[\w-]+$ naqshiga mos kelsa true qaytarsin. 3 ta to'g'ri va 2 ta noto'g'ri email bilan sinang. 7. preg_replace bilan telefon raqamidan ("+998 90 123 45 67") faqat raqamlarni qoldiring (/\D/ ni bo'sh matnga almashtiring). 8. preg_match va capture group bilan "2026-06-11" sanasidan yil, oy, kunni alohida o'zgaruvchilarga ajrating va chiqaring. 9. preg_match_all bilan "Narx: 100, 250, 999" matnidan barcha sonlarni toping va yig'indisini hisoblang. 10. preg_split bilan "olma, anor;uzum" matnini vergul yoki nuqta-vergul bo'yicha ajrating.

Qiyin 11. telefonToghrimi funksiyasini yozing: avval preg_replace bilan tozalang, keyin ^\+998\d{9}$ bilan tekshiring. Turli formatdagi 4 ta raqamga sinang. 12. parolKuchlimi funksiyasini yozing: kamida 8 belgi, katta harf, kichik harf va raqam bo'lsin (har shartni alohida regex bilan). 13. preg_match_all va capture group bilan matndan barcha email'larni ajrating: "Murojaat: ali@mail.com yoki vali@gmail.uz". 14. preg_match_all bilan post matnidan barcha hashtag'larni (#so'z) # belgisisiz ajrating. 15. Bitta funksiya yozing: sanaToghrimi(string $s): bool β€” YYYY-MM-DD formatini tekshirsin (oy 01-12, kun 01-31 oralig'ida, soddalashtirilgan holda).

Yechim β€” 6 (email validatsiya)
<?php
function emailToghrimi(string $email): bool
{
    return preg_match('/^[\w.+-]+@[\w-]+\.[\w-]+$/', $email) === 1;
}

var_dump(emailToghrimi("ali@mail.com"));     // true
var_dump(emailToghrimi("oqil@ioqil.uz"));    // true
var_dump(emailToghrimi("a.b@c.co"));         // true
var_dump(emailToghrimi("xato@mail"));        // false β€” nuqtali qism yo'q
var_dump(emailToghrimi("@mail.com"));        // false β€” nom yo'q

=== 1 ishlatdik, chunki preg_match 1 (topildi), 0 (yo'q) yoki false (xato) qaytaradi. === 1 aniq "topildi"ni bildiradi.

Yechim β€” 8 (sanadan qismlarni ajratish)
<?php
$sana = "2026-06-11";

if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $sana, $m)) {
    $yil = $m[1];
    $oy  = $m[2];
    $kun = $m[3];
    echo "Yil: {$yil}, Oy: {$oy}, Kun: {$kun}";
    // Yil: 2026, Oy: 06, Kun: 11
} else {
    echo "Sana formati noto'g'ri";
}

Har bir (...) qavs $m massivida alohida o'rin egallaydi: $m[1], $m[2], $m[3]. $m[0] esa butun moslik.

Yechim β€” 9 (sonlar yig'indisi)
<?php
$matn = "Narx: 100, 250, 999";

preg_match_all('/\d+/', $matn, $m);

var_dump($m[0]);            // ["100", "250", "999"]
echo array_sum($m[0]);     // 1349

preg_match_all barcha sonlarni $m[0] ga to'pladi, array_sum ularni qo'shdi (matn raqamlar avtomatik songa aylanadi).

Yechim β€” 11 (telefon validatsiya)
<?php
function telefonToghrimi(string $raqam): bool
{
    // Raqam va + dan boshqa hamma narsani tozalash
    $toza = preg_replace('/[^\d+]/', '', $raqam);

    // +998 + aynan 9 raqam
    return preg_match('/^\+998\d{9}$/', $toza) === 1;
}

var_dump(telefonToghrimi("+998901234567"));       // true
var_dump(telefonToghrimi("+998 90 123 45 67"));   // true
var_dump(telefonToghrimi("+998-90-1234567"));     // true
var_dump(telefonToghrimi("998901234567"));        // false β€” + yo'q

Avval preg_replace bilan barcha bo'sh joy, qavs, chiziqchani tozaladik, keyin qat'iy naqsh bilan tekshirdik. Bu β€” validatsiyada eng ishonchli yondashuv.

Yechim β€” 12 (kuchli parol)
<?php
function parolKuchlimi(string $parol): bool
{
    return strlen($parol) >= 8
        && preg_match('/[A-Z]/', $parol) === 1   // katta harf
        && preg_match('/[a-z]/', $parol) === 1   // kichik harf
        && preg_match('/\d/', $parol) === 1;     // raqam
}

var_dump(parolKuchlimi("Parol123"));   // true
var_dump(parolKuchlimi("parol123"));   // false β€” katta harf yo'q
var_dump(parolKuchlimi("Parol"));      // false β€” qisqa va raqam yo'q

Har bir shart β€” alohida kichik regex. && bilan bog'langan: bittasi false bo'lsa, butun natija false.

Yechim β€” 13 (matndan email'larni ajratish)
<?php
$matn = "Murojaat: ali@mail.com yoki vali@gmail.uz";

preg_match_all('/[\w.+-]+@[\w-]+\.[\w-]+/', $matn, $m);

var_dump($m[0]);
// ["ali@mail.com", "vali@gmail.uz"]

preg_match_all matn ichidagi naqshga mos barcha qismlarni $m[0] ga yig'di. Anchor (^ $) ishlatmadik β€” chunki email matn o'rtasida, boshi/oxirida emas.

Yechim β€” 14 (hashtag ajratish)
<?php
$post = "Bugun #php va #dasturlash, ayniqsa #regex zo'r!";

preg_match_all('/#(\w+)/', $post, $m);

var_dump($m[1]);
// ["php", "dasturlash", "regex"]  β€” # belgisisiz

#(\w+) β€” # belgisi va undan keyingi so'z. Qavs (\w+) so'zning o'zini (# siz) ushlab qoladi β€” shuning uchun $m[1] dan olamiz. $m[0] da esa # bilan bo'lardi.

Yechim β€” 15 (sana formati)
<?php
function sanaToghrimi(string $s): bool
{
    // Yil: 4 raqam
    // Oy: 01-12 (0 dan keyin 1-9, yoki 1 dan keyin 0-2)
    // Kun: 01-31
    $naqsh = '/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/';

    return preg_match($naqsh, $s) === 1;
}

var_dump(sanaToghrimi("2026-06-11"));   // true
var_dump(sanaToghrimi("2026-13-01"));   // false β€” 13-oy yo'q
var_dump(sanaToghrimi("2026-06-32"));   // false β€” 32-kun yo'q
var_dump(sanaToghrimi("2026-6-11"));    // false β€” oy 2 xonali emas

Bu yerda tanlov | va guruh (...) birgalikda ishladi: - Oy: 0[1-9] (01-09) yoki 1[0-2] (10-12). - Kun: 0[1-9] (01-09) yoki [12]\d (10-29) yoki 3[01] (30-31).

Bu soddalashtirilgan tekshiruv β€” masalan fevralda 30-kunni rad etmaydi. To'liq tekshiruv uchun checkdate() yoki DateTime ishlatiladi (keyingi bobda).