IlmHamroh
JavaScript Full-stack/12-qism. JavaScript: ilg'or mavzular va kod sifati8/44-dars21 daqiqa
Mundarija (36)

Unicode regex: u va v flag, \p{L} bilan har qanday alifbo va emoji

Qisqacha: u flagi regex'ni Unicode rejimiga o'tkazadi. Unda emoji bitta belgi bo'ladi va \p{...} — Unicode xususiyati — ishlay boshlaydi. Masalan, \p{L} — istalgan alifbodagi harf (lotin, kirill, xitoy), \p{N} — raqam, \p{Script=Cyrillic} — faqat kirill. v flagi u ning kengaytmasi: klasslar ustida amallar ([\p{L}--[a-z]] — "lotin kichik harfidan boshqa harf") va bir nechta kod nuqtasidan iborat emoji uchun \p{RGI_Emoji}.

Bu darsda

  • u flagi nimani o'zgartirishini va usiz \p{L} nega jimgina noto'g'ri ishlashini bilasiz.
  • \p{L}, \p{Lu}, \p{N}, \p{Script=Latin} bilan har qanday tildagi harf va raqamni topasiz.
  • O'zbekcha oʻ dagi belgi va oddiy apostrof regex uchun nega har xil ekanini tushuntirasiz.
  • Emoji'ni to'g'ri topasiz va \p{Emoji} ning tuzog'ini bilasiz.
  • v flagi bilan klasslarni ayirasiz va kesishtirasiz.
  • vazifalar dagi teglar xatosini Unicode regex bilan tuzatasiz.

Oldin bilishingiz kerak: Unicode va emoji: matn ichki tuzilishi, Belgilar va belgi klasslari, Flaglar va lastIndex tuzog'i, Lookahead va lookbehind.

1. Nega bu kerak?

«Bahor» menyusi ikki tilda: o'zbekcha (lotin) va ruscha. Mehmonlar sharhlarida esa emoji ko'p. Sardorga vazifa: menyudagi taom nomlarini ajratib, alifbo bo'yicha ro'yxat qilish. U bilgan vosita bilan urinib ko'rdi:

js
const menyu = "Osh — 35000 so'm, Плов, oʻrik sharbati";

console.log(menyu.match(/\w+/g));

Konsolda:

text
[ 'Osh', '35000', 'so', 'm', 'o', 'rik', 'sharbati' ]

Uchta muammo birdaniga. so'm ikkiga bo'lindi — bu tanish tuzoq. Ruscha Плов umuman topilmadi. oʻrik ham bo'lindi. Bu yerdagi ʻ belgisiga e'tibor bering — u oddiy apostrof emas, uni birozdan keyin ko'ramiz.

Belgilar va belgi klasslari darsida aytgan edik: \w faqat A–Z, a–z, 0–9 va _. Bu ingliz tili uchun yasalgan klass. Dunyoda esa yuzlab alifbo bor. Ularning hammasini taniydigan vosita — Unicode xususiyatlari. Ular faqat u yoki v flagi bilan ishlaydi.

Shu darsning oxirida vazifalar dagi haqiqiy xatoni ham tuzatamiz: "Non olish #bozor, #uy" matnida teg #bozor, — vergul bilan chiqadi.

2. u flagi: Unicode rejimi

2.1 Eslatma: kod nuqtasi va kod birligi

Unicode va emoji darsidan bilasiz: Unicode har belgiga raqam beradi — kod nuqtasi. JavaScript satri esa ichida 16 bitli kod birliklarini saqlaydi. Ko'p belgiga bitta birlik yetadi. Emoji kabi "katta" belgilar esa ikki birlikdan iborat — surrogat juftlik. Shuning uchun "🍵".length — 2.

Oddiy regex (flagsiz) satrni kod birliklari bo'yicha o'qiydi. U emoji'ni ikki bo'lak deb ko'radi. u flagi bilan esa regex kod nuqtalari bo'yicha o'qiydi: emoji — bitta belgi.

2.2 u nimani o'zgartiradi

Flaglar darsida u ning ikki ta'sirini ko'rgan edik: qat'iy sintaksis va emoji'ni butun olish. To'liq ro'yxat to'rtta:

u siz u bilan
. emoji'ning yarmini oladi . butun emoji'ni oladi
\u{1F375} — ishlamaydi \u{1F375} — kod nuqtasi bilan belgi ()
noto'g'ri yozuv jimgina oddiy matn noto'g'ri yozuv — SyntaxError
\p{L} — p{L} matni \p{L} — Unicode xususiyati

Ikkinchi qatorni sinab ko'ramiz. \u{...} — belgini kod nuqtasi bilan yozish, Unicode va emoji darsidagi String.fromCodePoint ning regex'dagi o'xshashi:

js
console.log(/\u{1F375}/u.test("Choy 🍵")); // true
console.log(/^.$/.test("🍵")); // false
console.log(/^.$/u.test("🍵")); // true

^.$ — "matn aynan bitta belgidan iborat". u siz emoji ikki belgi deb hisoblandi va naqsh mos kelmadi.

2.3 Eng xavfli tuzoq: u siz \p{L}

To'rtinchi qator — eng xavflisi. u ni unutsangiz, xato chiqmaydi:

js
console.log(/\p{L}/.test("Osh")); // false
console.log(/\p{L}/.test("p{L}")); // true

Flagsiz regex'da \p — shunchaki p harfi ("noma'lum escape" — \ tashlab yuboriladi). {L} esa oddiy matn. Natijada naqsh "p{L} degan so'z"ni qidiradi. Kod ishlaydi, lekin mutlaqo boshqa narsa qiladi.

Qoida: \p{...} ko'rgan joyingizda flaglarga qarang — u yoki v bo'lishi shart. Yangi yozilayotgan regex'da u qo'yishni odat qiling: u bunday jim xatolarni SyntaxError ga aylantiradi.

Tekshirib ko'ring: "🍎🍵".match(/./g).length va "🍎🍵".match(/./gu).length nima qaytaradi?

Javob

4 va 2. Har emoji — ikki kod birligi. u siz nuqta har birligini alohida belgi deb oldi. u bilan — har emoji bitta kod nuqtasi, demak bitta belgi.

3. Unicode xususiyatlari: \p{...}

3.1 \p{L} — istalgan alifbodagi harf

Unicode har bir belgiga raqamdan tashqari xususiyatlar ham bergan: bu harfmi, raqammi, tinish belgisimi, qaysi yozuvga tegishli. Unicode xususiyati (Unicode property escape) — regex'da shu xususiyat bo'yicha belgi tanlash. Yozilishi: \p{Nomi}.

Kutubxonadagi kitoblarni eslang. Har kitob kartochkasida janri yozilgan: "roman", "she'r", "lug'at". Kutubxonachi "menga hamma romanlarni ber" desa, sarlavhalarni bittalab o'qimaydi — kartochkaga qaraydi. \p{L} ham shunday: "kartochkasida «harf» deb yozilgan hamma belgilar".

L — "Letter" (harf). Maydonda sinang:

Uch narsaga qarang. Birinchidan, ruscha Плов va xitoycha 抓饭 ham "harf". Ikkinchidan, oʻrik butun qoldi — demak ʻ belgisi harf. Uchinchidan, so'm baribir bo'lindi — oddiy apostrof harf emas. Bu farqni alohida bo'limda ko'ramiz.

3.2 Asosiy kategoriyalar

L — umumiy kategoriya (general category). Eng ko'p kerak bo'ladiganlari:

Yozuv Ma'nosi Misol
\p{L} harf a, Ш, 抓
\p{Lu} / \p{Ll} katta / kichik harf O, П / o, п
\p{N} raqam (har qanday yozuvda) 5, ٣, ३
\p{P} tinish belgisi ,, !, '

Lu — "Letter, uppercase" (katta harf), Ll — "Letter, lowercase" (kichik harf). Xuddi shu tartibda Nd — o'nlik raqam, Lm — "modifikator harf" (birozdan keyin kerak bo'ladi). Uzun nomlar ham ishlaydi: \p{Letter}, \p{Uppercase_Letter}.

Katta P — teskarisi. \P{L} — "harf bo'lmagan har qanday belgi", xuddi \D va \d kabi.

O'zingiz to'ldiring: faqat kichik harflarni topish uchun \p{} yoziladi.

Raqamlar haqida bitta muhim farq:

js
const matn = "35000 ٣٥ ३५";

console.log(matn.match(/\p{N}+/gu)); // [ '35000', '٣٥', '३५' ]
console.log(matn.match(/\d+/gu)); // [ '35000' ]

\p{N} arab va hind raqamlarini ham oldi. \d esa u bilan ham faqat 0–9. Narx yoki telefon maydonida aynan \d kerak: Number("٣٥") — NaN.

Noto'g'ri nom yozsangiz, u rejimi darhol aytadi:

js
const naqsh = new RegExp("\\p{Harf}", "u");
text
SyntaxError: Invalid regular expression: /\p{Harf}/u: Invalid property name

Tarjimasi: "Noto'g'ri xususiyat nomi." Nomlar Unicode standartidan olinadi va inglizcha — o'zingiz o'ylab topa olmaysiz. To'liq ro'yxat MDN'da, "Unicode character class escape" sahifasida.

3.3 \p{Script=...} — qaysi yozuv

Ba'zan "har qanday harf" emas, aniq yozuv kerak: faqat lotin yoki faqat kirill. Buning uchun Script (yozuv) xususiyati bor:

Qisqa shakli — \p{sc=Cyrillic} (sc — "script"). Ko'p ishlatiladigan nomlar: Latin, Cyrillic, Arabic, Han (xitoy ierogliflari), Greek.

Bu qayerda kerak? Masalan, homoglif — ko'rinishi bir xil, lekin har xil alifbodagi harflar. Kirillcha а va lotincha a ko'zga bir xil, kompyuter uchun esa har xil. Firibgarlar shu bilan soxta saytlar nomini yasaydi. Bir so'zda ham lotin, ham kirill bo'lsa — shubhali. Buni 2-mashqda tekshiramiz.

3.4 O'zbek apostrofi: ʻ va '

Endi oʻrik siriga qaytamiz. O'zbek lotin alifbosining rasmiy yozuvida oʻ va gʻ dagi belgi — U+02BB ("modifier letter turned comma"). U oddiy apostrof ' (U+0027) emas. Klaviaturada ko'pincha uning o'rniga ' yoki ’ (U+2019) yoziladi. Ko'zga uchalasi deyarli bir xil — Unicode uchun esa har xil:

js
console.log(/\p{L}/u.test("ʻ")); // true
console.log(/\p{L}/u.test("'")); // false
console.log(/\p{L}/u.test("\u{2019}")); // false
console.log(/\p{Lm}/u.test("ʻ")); // true
Belgi Kod Unicode bo'yicha
ʻ U+02BB harf — Lm (modifikator harf)
' U+0027 tinish belgisi — P
’ U+2019 tinish belgisi — P

Amaliy xulosa: o'zbekcha so'zni to'liq olish uchun [\p{L}'] — "harf yoki oddiy apostrof". ʻ ga alohida qoida kerak emas, u allaqachon \p{L} ichida. ’ ham kerak bo'lsa — klassga qo'shasiz: [\p{L}'’].

Yana bir nozik joy: \p{Script=Latin} ʻ ni olmaydi. U Unicode'da "umumiy" (Common) yozuvga tegishli — bir nechta alifbo ishlatadi. Shuning uchun \p{Script=Latin}+ oʻrik ni o va rik ga bo'ladi. O'zbekcha matn uchun \p{L} ishonchliroq.

3.5 Birikuvchi belgilar

Unicode va emoji darsida normalize() ni ko'rgan edik: Ў harfi bitta kod nuqtasi ham, ikkitasi ham (У + birikuvchi belgi) bo'lishi mumkin. Ikkinchi holatda \p{L} so'zni bo'lib yuboradi — birikuvchi belgi harf emas, u \p{M} ("mark"):

js
const yoyilgan = "Ўзбек".normalize("NFD");

console.log(yoyilgan.match(/\p{L}+/gu)); // [ 'У', 'збек' ]
const yigilgan = yoyilgan.normalize("NFC");
console.log(yigilgan.match(/\p{L}+/gu)); // [ 'Ўзбек' ]

Yechim — regex'dan oldin normalize("NFC"), yoki klassga \p{M} ni qo'shish: [\p{L}\p{M}]+. Foydalanuvchidan kelgan matnda birinchisi odatiy yo'l.

3.6 Har tilga mos so'z chegarasi

Langarlar va so'z chegarasi darsida \b ikki joyda aldagan edi: apostrof yonida va kirill matnda. Lookahead va lookbehind darsida lotin uchun (?<![\w'])...(?![\w']) yasadik va "har tilga moslarini Unicode darsida" degan edik. Endi \w o'rniga [\p{L}\p{N}_']:

Qoida: "so'z belgisi" nima ekanini o'zingiz belgilaysiz — [\p{L}\p{N}_'] — va ikki tomonga inkor lookaround qo'yasiz. \b faqat ASCII \w ni biladi va u bilan ham o'zgarmaydi.

Tekshirib ko'ring: "Ош osh 抓饭".match(/\p{L}+/gu) va "Ош osh 抓饭".match(/\p{Script=Latin}+/gu) nima qaytaradi?

Javob

Birinchisi — [ 'Ош', 'osh', '抓饭' ]: uch yozuvdagi harflarning hammasi. Ikkinchisi — [ 'osh' ]: faqat lotin yozuvi.

4. Emoji

4.1 \p{Emoji} tuzog'i

Emoji uchun ham xususiyat bor — \p{Emoji}. Lekin uning nomi aldaydi:

js
const matn = "Choy 🍵, stol #4";

console.log(matn.match(/\p{Emoji}/gu)); // [ '🍵', '#', '4' ]

# va 4 ham "emoji"! Sababi: ular emoji ketma-ketligining boshi bo'la oladi (klaviatura tugmasi emoji'si # va raqamdan yig'iladi). Unicode ularni Emoji xususiyati bilan belgilagan. Ekranda rangli rasm sifatida chiqadigan emoji'lar uchun boshqa xususiyat bor — \p{Emoji_Presentation}:

js
const matn = "Choy 🍵, stol #4";

console.log(matn.match(/\p{Emoji_Presentation}/gu)); // [ '🍵' ]

4.2 Bir nechta kod nuqtasidan yig'ilgan emoji

Unicode va emoji darsida ko'rgan edingiz: ba'zi emoji'lar bir nechta kod nuqtasidan yig'iladi. Masalan, bayrog'i — ikkita maxsus harf. — qo'l va teri rangi. Oila emoji'si esa — uchta odam va ular orasidagi "bog'lovchi" belgilar. \p{Emoji_Presentation} bitta kod nuqtasini oladi, shuning uchun bunday emoji bo'laklarga bo'linadi:

js
const xabar = "Rahmat 👍🏽 🇺🇿";

console.log(xabar.match(/\p{Emoji_Presentation}/gu).length); // 4

Ko'zga ikkita emoji, regex uchun — to'rtta bo'lak.

4.3 v flagi va \p{RGI_Emoji}

Bu muammoni v flagi hal qiladi. Unda satr xususiyatlari (properties of strings) bor — bitta belgini emas, butun ketma-ketlikni moslaydigan xususiyatlar. Eng foydalisi — \p{RGI_Emoji}: Unicode tavsiya qilgan (RGI — "Recommended for General Interchange") har qanday to'liq emoji:

Emoji'ni matndan olib tashlash ham shunday:

js
const sharh = "Choy tayyor 🍵👍🏽 Bahor 🇺🇿!";

const toza = sharh.replace(/\p{RGI_Emoji}/gv, "");

console.log(toza); // Choy tayyor  Bahor !

Satr xususiyatlari faqat v bilan ishlaydi. u bilan \p{RGI_Emoji} yozsangiz — Invalid property name.

Maslahat: "Ekranda nechta belgi?" degan savolga regex emas, Intl.Segmenter javob beradi (Unicode va emoji darsidan). \p{RGI_Emoji} esa emoji'ni topish, sanash yoki olib tashlash uchun.

5. v flagi: klasslar ustida amallar

5.1 v nima

v (unicode sets — "to'plamlar") — u ning 2023-yilda qo'shilgan yangi varianti. U u qila oladigan hamma narsani qiladi va ustiga uchta imkoniyat qo'shadi: satr xususiyatlari (\p{RGI_Emoji}), klasslar ustida amallar va qat'iyroq klass sintaksisi.

u va v ni birga yozib bo'lmaydi — v allaqachon u ni o'z ichiga oladi:

js
const naqsh = new RegExp("\\p{L}", "uv");
text
SyntaxError: Invalid flags supplied to RegExp constructor 'uv'

Tarjimasi: "RegExp konstruktoriga noto'g'ri flaglar berildi." Ikkalasidan bittasini tanlang.

5.2 Ayirish -- va kesishish &&

To'plamlar darsida Set uchun difference va intersection ni ko'rgan edingiz. v flagi xuddi shu amallarni belgi klasslari uchun beradi:

  • [A--B] — ayirish: A dagi, lekin B da yo'q belgilar;
  • [A&&B] — kesishish: ham A da, ham B da bor belgilar.
js
const nom = "Plov, Плов, 抓饭";

console.log(nom.match(/[\p{L}--[a-zA-Z]]+/gv)); // [ 'Плов', '抓饭' ]

[\p{L}--[a-zA-Z]] — "har qanday harf, lekin ASCII lotin harfi emas". Ayirilayotgan qism o'zi ham klass — [a-zA-Z] — shuning uchun qavs ichida qavs. Ichma-ich klass faqat v da mumkin.

Kesishishga misol — "kirill katta harfi":

js
console.log("Ош Плов osh".match(/[\p{Lu}&&\p{Script=Cyrillic}]/gv));
// [ 'О', 'П' ]

Bunday narsalarni u bilan yozish juda qiyin yoki imkonsiz edi.

Maslahat: v klassiga bir nechta kod nuqtali satrni ham qo'yish mumkin — \q{...} bilan. [\q{🇺🇿|🇰🇿}] — "yoki O'zbekiston bayrog'i, yoki Qozog'iston bayrog'i". Bayroq ikki kod nuqtasi bo'lgani uchun oddiy klassga sig'maydi. Kam kerak bo'ladi — uchratsangiz, taniysiz.

5.3 Qat'iyroq klass sintaksisi

v klass ichida ba'zi belgilarni escape qilishni talab qiladi. Oddiy klassda [(] — "qavs" edi. v da esa:

js
const naqsh = new RegExp("[(]", "v");
text
SyntaxError: Invalid regular expression: /[(]/v: Invalid character in character class

Tarjimasi: "Belgi klassida noto'g'ri belgi." v da klass ichidagi (, ), [, ], {, }, /, -, | — escape bilan: [\(]. Ikkilangan tinish belgilari (&&, --, !!) esa amallar uchun band. Sabab — kelajakda yangi sintaksis qo'shilganda eski kod buzilmasligi.

Shuning uchun u dagi regex'ni v ga o'tkazishda klasslarni ko'zdan kechiring: [(-] kabi joylar xato beradi.

5.4 Qayerda ishlaydi

Baseline ma'lumoti bo'yicha:

  • u flagi va \u{...} — 2016-yildan barcha brauzerlarda;
  • \p{...} xususiyatlari — 2020-yil iyulidan (Chrome 64, Firefox 78, Safari 11.1);
  • v flagi — 2023-yil sentyabridan (Chrome 112, Firefox 116, Safari 17), 2026-yil martidan esa "keng tarqalgan" (Baseline widely available).

Node 24 va Chrome 154 da bu darsdagi hamma misolni sinadik — natijalar bir xil. HTML'ning pattern atributi naqshni v flagi bilan tekshiradi (Mobil klaviatura va kiritish yordamchilari), shuning uchun pattern ichida ham \p{L} yozish mumkin.

Qaysi birini tanlash kerak? Oddiy holatda — u: u ko'proq tanish va ko'p kodda uchraydi. To'plam amallari yoki \p{RGI_Emoji} kerak bo'lsa — v.

Tekshirib ko'ring: [\p{L}&&\p{Ll}] ni v siz yozish mumkinmi? Uning qisqaroq yozuvi bormi?

Javob

v siz && amal emas — u bilan ham, flagsiz ham boshqacha o'qiladi. Lekin bu misolda amal kerak ham emas: kichik harfning hammasi baribir harf, demak kesishish — shunchaki \p{Ll}. Amallar bir to'plam ikkinchisining qismi bo'lmaganda foydali: [\p{Lu}&&\p{Script=Cyrillic}].

6. Ko'p uchraydigan xatolar

6.1 \p{...} ni flagsiz yozish

/\p{L}+/g xato bermaydi, lekin p{L} matnini qidiradi. Tuzatish: \p bor regex'da har doim u yoki v. ESLint'ning require-unicode-regexp qoidasi (standart to'plamda o'chiq — o'zingiz yoqasiz) har regex'da u yoki v talab qiladi; ko'p jamoalar uni yoqadi (ESLint va Prettier).

6.2 ʻ va ' ni bir xil deb o'ylash

[\p{L}'] oʻrik ni ham, o'rik ni ham oladi, lekin bular har xil satrlar: "oʻrik" === "o'rik" — false. Qidiruv va solishtirishda avval apostroflarni bir xil ko'rinishga keltirish kerak — buni Almashtirish chuqur darsida replace bilan qilamiz.

6.3 \p{Emoji} bilan emoji qidirish

U #, * va raqamlarni ham oladi. Tuzatish: rangli emoji uchun \p{Emoji_Presentation} (u), to'liq emoji uchun \p{RGI_Emoji} (v).

6.4 \d ni \p{N} ga almashtirish

"Unicode yaxshiroq" deb \d o'rniga \p{N} yozsangiz, narx maydoni ٣٥ ni ham qabul qiladi va Number NaN beradi. Tuzatish: son kerak bo'lgan joyda \d.

6.5 Normalizatsiyasiz \p{L}

Boshqa dasturdan nusxalangan matnda Ў ikki kod nuqtasi bo'lishi mumkin. Tuzatish: matn.normalize("NFC") regex'dan oldin.

O'zingiz to'ldiring: kirill harflarini topish uchun \p{Script=} yoziladi.

7. Mashqlar

1-mashq (oson): Harflar soni

harflarSoni(matn) matndagi harflar sonini qaytarsin — har qanday alifbodagi. Raqam, bo'sh joy, tinish belgisi va emoji sanalmasin.

js
console.log(harflarSoni("Osh — 35000 so'm")); // 6
console.log(harflarSoni("Ош 🍵")); // 2
Yechim
js
function harflarSoni(matn) {
  return (matn.match(/\p{L}/gu) ?? []).length;
}

console.log(harflarSoni("Osh — 35000 so'm")); // 6
console.log(harflarSoni("Ош 🍵")); // 2
console.log(harflarSoni("oʻrik")); // 5

?? [] — harf bo'lmasa match null qaytaradi. Uchinchi qatorga qarang: oʻrik da 5 ta harf, chunki ʻ ham harf. o'rik da esa 4 ta bo'lardi.

2-mashq (o'rta): Aralash alifboli so'z

aralashmi(soz) — so'zda ham lotin, ham kirill harfi bo'lsa true qaytarsin. Bu homoglif (soxta harf) belgisi. Ishora: ikkita test — \p{Script=Latin} va \p{Script=Cyrillic}.

Sinov uchun so'zlarni kod nuqtalari bilan yasaymiz — \u{...} satrda ham ishlaydi. \u{41C} — kirillcha katta М, ko'zga lotin M dan farqi yo'q.

js
const sozlar = ["\u{41C}asalan", "Masalan", "Москва", "bozor"];
console.log(sozlar.map(aralashmi)); // [ true, false, false, false ]
Yechim
js
function aralashmi(soz) {
  const lotin = /\p{Script=Latin}/u.test(soz);
  const kirill = /\p{Script=Cyrillic}/u.test(soz);
  return lotin && kirill;
}

const sozlar = ["\u{41C}asalan", "Masalan", "Москва", "bozor"];
console.log(sozlar.map(aralashmi)); // [ true, false, false, false ]

Birinchi so'z ko'zga Masalan ga o'xshaydi, lekin boshidagi harf kirillcha. Matndagi homogliflarni topadigan dasturlar va domen nomlaridagi firibgarlikni ushlaydigan tizimlar shu g'oyaga tayanadi.

3-mashq (qiyin): Emoji statistikasi

emojiSoni(matn) matndagi to'liq emoji'lar sonini qaytarsin (bayroq va teri rangli qo'l — bittadan). emojisiz(matn) esa emoji'larni olib tashlab, ortiqcha bo'sh joylarni bittaga keltirsin. Ishora: \p{RGI_Emoji} faqat v flagi bilan; bo'sh joylar — \s+.

js
const sharh = "Choy tayyor 🍵👍🏽 bilan keldik 🇺🇿";
console.log(emojiSoni(sharh)); // 3
console.log(emojisiz(sharh)); // Choy tayyor bilan keldik
Yechim
js
const EMOJI = /\p{RGI_Emoji}/gv;

function emojiSoni(matn) {
  return (matn.match(EMOJI) ?? []).length;
}

function emojisiz(matn) {
  return matn.replace(EMOJI, "").replace(/\s+/g, " ").trim();
}

const sharh = "Choy tayyor 🍵👍🏽 bilan keldik 🇺🇿";
console.log(emojiSoni(sharh)); // 3
console.log(emojisiz(sharh)); // Choy tayyor bilan keldik
console.log(sharh.length); // 36

sharh.length — 36: kod birliklari, ulardan 10 tasi uchta emoji ichida. \p{Emoji_Presentation} bilan sanasangiz, 5 chiqardi — bo'laklar soni. EMOJI regex'i ikki funksiyada ulashilgan: match va replace lastIndex ni o'zi tozalaydi, shuning uchun bu xavfsiz (Flaglar va lastIndex tuzog'i).

4-mashq: Vazifalar qadami — teglar xatosini tuzatish

Bu safar vazifalar ning o'zini tuzatamiz. Branch: fix/teglar-regex.

Xato. royxat.js dagi teglariniOl matnni split(" ") bilan bo'ladi — bo'sh joygacha hammasi teg:

js
const matn = "Sabzi #bozor, #uy.";

console.log(
  matn.split(" ").filter((s) => s.length > 1 && s.startsWith("#")),
);
// [ '#bozor,', '#uy.' ]

Brauzerda (v3, haqiqiy chiqish) teglar qatori shunday: Teglar: #bozor — 1, #bozor, — 1, #uy — 1. Bitta teg ikki xil bo'lib qoldi. Xuddi shunday #uy., #bozor🛒, ##, ##bozor ham noto'g'ri teg bo'ladi.

Ko'prik: o'tgan darslardagi naqsh. Lookahead va lookbehind darsidagi mashqda (?<![\w'])#[\w']+ yozgan edik. U vergulni yechadi, lekin vazifalar uchun uchta joyda yetmaydi:

js
const naqsh = /(?<![\w'])#[\w']+/g;

console.log("Sabzi #bozor, #uy.".match(naqsh)); // [ '#bozor', '#uy' ]
console.log("#ДОМ #қўй".match(naqsh)); // null
console.log("#oʻquv".match(naqsh)); // [ '#o' ]
console.log("'#o'quv' ##bozor".match(naqsh)); // [ '#bozor' ]
  1. \w faqat ASCII — kirill tegi umuman yo'q, #oʻquv esa #o da kesildi (ʻ — \w emas). Yechim — [\p{L}\p{N}_] va u flagi.
  2. Apostrof klass ichida — u istalgan joyda turishi mumkin. '#o'quv' da teg oldidagi ' lookbehind'ni to'xtatdi. Yechim — apostrof faqat ikki harf orasida: (?:['‘’][\p{L}\p{N}_]+)*.
  3. ##bozor dan #bozor chiqdi — ikkinchi # oldida # turibdi, u esa [\w'] emas. Yechim — lookbehind'ga # ni ham qo'shish.

Yechim — u flagli regex. Funksiya nomi, parametri va natijasi (Set, kichik harf) o'zgarmaydi, faqat ichi:

js
// Oldin (v3, royxat.js)
function teglariniOl(matn) {
  return new Set(
    matn
      .toLowerCase()
      .split(" ")
      .filter((soz) => soz.length > 1 && soz.startsWith("#")),
  );
}
js
// Keyin
// Teg: # dan keyin harf, raqam yoki _ — har qanday alifbo (\p{L}, u
// flagi). Apostrof faqat harflar orasida: #o'quv, #g'isht. ʻ (U+02BB)
// \p{L} ga kiradi; ' ‘ ’ — tinish belgisi, alohida. Oldida harf
// yoki # bo'lsa (Non#uy, ##uy) — teg emas
const TEG =
  /(?<![\p{L}\p{N}_#])#[\p{L}\p{N}_]+(?:['‘’][\p{L}\p{N}_]+)*/gu;

function teglariniOl(matn) {
  const teglar = matn.match(TEG) ?? [];
  return new Set(teglar.map((teg) => teg.toLowerCase()));
}

Naqshni bo'laklab o'qiymiz:

Bo'lak Ma'nosi Nega
# panjara —
[\p{L}\p{N}_]+ istalgan alifbodagi harf, raqam, _ \w #қўй, #дом ni kesadi; \p{…} faqat u (yoki v) bilan
(?:['‘’][\p{L}\p{N}_]+)* apostrof — faqat ikki harf orasida #o'quv, #g‘isht, #o’quv butun; '#o'quv' dagi oxirgi ' tegga kirmaydi
(?<![\p{L}\p{N}_#]) oldida harf, raqam yoki # bo'lmasin Non#uy, ##bozor — teg emas

ʻ (U+02BB) va ʼ (U+02BC) klassga yozilmagan: ular Unicode'da harf (Lm), allaqachon \p{L} ichida. Oxirgi g — hammasini topish uchun; moslik yo'q bo'lsa match null qaytaradi, shuning uchun ?? [].

Ikki nozik joy. Birinchisi — toLowerCase() endi moslikdan keyin chaqiriladi. Avval kichik harfga o'tkazilsa, #İstanbul dagi İ ikki kod nuqtasiga (i + birikuvchi belgi) aylanib, regex tegni #i da kesardi. Ikkinchisi — v flagi ham shu naqsh bilan bir xil natija beradi (sinab ko'rildi), lekin to'plam amallari bu yerda kerak emas — shuning uchun u. Emoji (🛒) \p{L} emas, tegga kirmaydi.

Ataylab qilinmagan narsa ham bor: #o'quv va #oʻquv hali ham ikki xil teg. Apostroflarni bir xil ko'rinishga keltirish ilovaning xulqini kengaytiradi, bunday talab esa yo'q.

const TEG = dan keyin qator bo'lingani — ataylab. Kursda keyinroq o'rnatiladigan Prettier (ESLint va Prettier) aynan shunday yozadi.

Tekshiruv. Avtomatik testlar hali yo'q (Birinchi avtomatik test darsida keladi), shuning uchun v3 dagi tekshiruv skriptidan foydalanamiz. tekshiruv/royxat.js oxiriga:

js
// Teglar (12/#08): bitta vazifadagi teglar, bo'sh joy bilan
const teglar = (matn) => [...VazifalarRoyxati
  .dan([{ id: 1, matn, bajarildi: false }])
  .tegHisobi().keys()].join(" ");
tekshir("teg: vergul, nuqta", teglar("Sabzi #bozor, #uy."),
  "#bozor #uy");
tekshir("teg: o'zbekcha", teglar("#o'quv #g'isht #oʻquv #g’isht"),
  "#o'quv #g'isht #oʻquv #g’isht");
tekshir("teg: emoji", teglar("#bozor🛒 #🛒"), "#bozor");
tekshir("teg emas: # ## Non#uy", teglar("# ## Non#uy"), "");
tekshir("teg: katta harf", teglar("#BOZOR #Bozor"), "#bozor");

tekshir(nomi, haqiqiy, kutilgan) — skriptdagi yordamchi: teng bo'lsa , aks holda chiqaradi. Haqiqiy chiqish — oldin (v3 kodi bilan, chapda) va keyin (o'ngda):

text
❌ teg: vergul, nuqta: #bozor, #uy.                   ✅ teg: vergul, nuqta: #bozor #uy
✅ teg: o'zbekcha: #o'quv #g'isht #oʻquv #g’isht      ✅ teg: o'zbekcha: #o'quv #g'isht #oʻquv #g’isht
❌ teg: emoji: #bozor🛒 #🛒                           ✅ teg: emoji: #bozor
❌ teg emas: # ## Non#uy: ##                          ✅ teg emas: # ## Non#uy: 
✅ teg: katta harf: #bozor                            ✅ teg: katta harf: #bozor

#o'quv v3 da ham ishlardi — teglar bo'sh joy bilan ajralgani uchun. Bu test yangi regex uni buzmasligini qotirib qo'yadi. Qolgan tekshiruvlar (royxat, paket, saqlash, marshrut skriptlari) v3 bilan bayt-bayt bir xil.

Brauzerda: import'dan keyin "Sabzi #bozor, #uy" vazifasi qo'shiladi va teglar qatori tekshiriladi:

text
v3:        ❌ teglar: vergulsiz: "Teglar: #bozor — 1, #bozor, — 1, #uy — 1" (kutilgan "Teglar: #bozor — 2, #uy — 1")
v3 + #08:  ✅ teglar: vergulsiz: "Teglar: #bozor — 2, #uy — 1"        → 45/45 "HAMMASI ✅"

Qolgan 44 tekshiruv ikkalasida ham bir xil .

Yechim (buyruqlar)
bash
git switch -c fix/teglar-regex
node tekshiruv/royxat.js
git add assets/js/royxat.js tekshiruv/royxat.js
git commit -m "fix: teglar regex bilan ajratiladi — \"#bozor,\" dagi \
vergul tegga yopishmaydi, o'zbekcha apostrof saqlanadi"
git push -u origin fix/teglar-regex
gh pr create --fill
gh pr merge --merge

Commit xabaridagi \ va yangi qator — terminal uchun: qo'shtirnoq ichida ular olib tashlanadi va xabar bitta qator bo'lib qoladi. Bu branch nomi fix/ bilan boshlanadi: bu safar biz xulqni o'zgartirdik — xatoni tuzatdik. Refaktor (xulq o'zgarmaydi) esa refactor/ bilan bo'ladi, buni qismning oxiridagi darslarda ko'rasiz.

8. Real ishda

  • Ko'p tilli saytlar. O'zbekistondagi loyihalarda matn lotin, kirill va rus tilida aralash keladi. Qidiruv, teglar, ism tekshiruvi — hammasida \w emas, \p{L} kerak.
  • Xavfsizlik. Homoglif hujumlari (аpple.com dagi kirillcha а) — brauzerlar va pochta xizmatlari aralash yozuvli domenlarni aynan Script xususiyati bilan aniqlaydi.
  • Ijtimoiy tarmoq va chatlar. Telegram kabi ilovalarda emoji'ni sanash, xabar uzunligini cheklash, hashtag ajratish — \p{RGI_Emoji} va \p{L} bilan.
  • Intervyu. "u flagi nima qiladi?", "Har qanday tildagi harfni qanday topasiz?", "Nega '😀'.length — 2?" — tez-tez beriladi.

Xulosa

  • u — Unicode rejimi: emoji bitta belgi, \u{...}, qat'iy sintaksis, \p{...}. Usiz \p{L} jimgina p{L} matnini qidiradi.
  • \p{L} — har qanday harf, \p{Lu}/\p{Ll} — katta/kichik, \p{N} — har qanday raqam, \P{...} — teskarisi; \p{Script=Cyrillic} — aniq yozuv.
  • O'zbekcha ʻ (U+02BB) — harf, ' va ’ — tinish belgisi; o'zbekcha so'z uchun [\p{L}'].
  • \p{Emoji} raqam va # ni ham oladi; rangli emoji — \p{Emoji_Presentation}, to'liq emoji — \p{RGI_Emoji} (v).
  • v — u ning kengaytmasi: -- va && amallari, ichma-ich klass, \q{...}; klass ichida ( kabi belgilar escape bilan; u va v birga emas.
  • So'z chegarasi har tilda: (?<![\p{L}\p{N}_'])...(?![\p{L}\p{N}_']).

Keyingi dars: Almashtirish chuqur va xavfsiz dinamik regex — $1, $<nom>, funksiya bilan almashtirish va foydalanuvchi matnidan RegExp.escape bilan xavfsiz regex yasash.

Manbalar

  • MDN: "Unicode character class escape: \p{...}, \P{...}", "RegExp.prototype.unicode", "RegExp.prototype.unicodeSets" — developer.mozilla.org
  • Unicode Standard Annex #44 (UCD: General_Category, Script), Unicode Technical Standard #51 (Emoji, RGI) — unicode.org
  • TC39: "RegExp v flag with set notation and properties of strings" — github.com/tc39/proposal-regexp-v-flag
  • web-features 3.40.0: javascript.builtins.RegExp.unicodeSets — Baseline 2023-09-18, widely 2026-03-18
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Unicode regex: u va v flag, \p{L} bilan har qanday alifbo va emoji — IlmHamroh