Raziščite napredno ujemanje vzorcev v JavaScriptu z regularnimi izrazi. Spoznajte sintakso, praktično uporabo in tehnike optimizacije za učinkovito in robustno kodo.
Ujemanje vzorcev v JavaScriptu z regularnimi izrazi: Celovit vodnik
Regularni izrazi (regex) so močno orodje za ujemanje vzorcev in manipulacijo besedila v JavaScriptu. Razvijalcem omogočajo iskanje, preverjanje in preoblikovanje nizov na podlagi določenih vzorcev. Ta vodnik ponuja celovit pregled regularnih izrazov v JavaScriptu, ki zajema sintakso, uporabo in napredne tehnike.
Kaj so regularni izrazi?
Regularni izraz je zaporedje znakov, ki določa iskalni vzorec. Ti vzorci se uporabljajo za ujemanje in manipulacijo nizov. Regularni izrazi se v programiranju pogosto uporabljajo za naloge, kot so:
- Validacija podatkov: Zagotavljanje, da se uporabniški vnos ujema z določenimi formati (npr. e-poštni naslovi, telefonske številke).
- Ekstrakcija podatkov: Pridobivanje določenih informacij iz besedila (npr. pridobivanje datumov, URL-jev ali cen).
- Iskanje in zamenjava: Iskanje in zamenjava besedila na podlagi kompleksnih vzorcev.
- Obdelava besedil: Razdeljevanje, združevanje ali preoblikovanje nizov na podlagi določenih pravil.
Ustvarjanje regularnih izrazov v JavaScriptu
V JavaScriptu lahko regularne izraze ustvarimo na dva načina:
- Uporaba literala regularnega izraza: Vzorec zapišemo med poševnicama (
/). - Uporaba konstruktorja
RegExp: Ustvarimo objektRegExpz vzorcem v obliki niza.
Primer:
// Uporaba literala regularnega izraza
const regexLiteral = /hello/;
// Uporaba konstruktorja RegExp
const regexConstructor = new RegExp("hello");
Izbira med obema metodama je odvisna od tega, ali je vzorec znan ob prevajanju ali pa je dinamično generiran. Uporabite literal, ko je vzorec fiksen in znan vnaprej. Uporabite konstruktor, ko je treba vzorec zgraditi programsko, še posebej pri vključevanju spremenljivk.
Osnovna sintaksa regularnih izrazov
Regularni izrazi so sestavljeni iz znakov, ki predstavljajo vzorec, ki ga je treba ujeti. Tukaj je nekaj osnovnih komponent regularnih izrazov:
- Dobesedni znaki: Ujemajo se z znaki samimi (npr.
/a/se ujema z znakom 'a'). - Metaznaki: Imajo poseben pomen (npr.
.,^,$,*,+,?,[],{},(),\,|). - Znaki razreda: Predstavljajo skupine znakov (npr.
[abc]se ujema z 'a', 'b' ali 'c'). - Kvantifikatorji: Določajo, kolikokrat se mora pojaviti znak ali skupina (npr.
*,+,?,{n},{n,},{n,m}). - Sidra: Ujemajo se s položaji v nizu (npr.
^se ujema z začetkom,$se ujema s koncem).
Pogosti metaznaki:
.(pika): Ujema se s katerim koli posameznim znakom, razen z znakom za novo vrstico.^(strešica): Ujema se z začetkom niza.$(dolar): Ujema se s koncem niza.*(zvezdica): Ujema se z nič ali več pojavitvami predhodnega znaka ali skupine.+(plus): Ujema se z eno ali več pojavitvami predhodnega znaka ali skupine.?(vprašaj): Ujema se z nič ali eno pojavitvijo predhodnega znaka ali skupine. Uporablja se za neobvezne znake.[](oglat oklepaj): Določa razred znakov, ki se ujema s katerim koli posameznim znakom znotraj oklepajev.{}(zavit oklepaj): Določa število pojavitev, ki se morajo ujemati.{n}se ujema natančno n-krat,{n,}se ujema n-krat ali večkrat,{n,m}se ujema med n- in m-krat.()(okrogel oklepaj): Združuje znake in zajame ujemajoči se podniz.\(poševnica nazaj): Ubeži metaznakom, kar omogoča, da se ujemajo dobesedno.|(pokončna črta): Deluje kot operator "ali", ki se ujema z izrazom pred ali za njim.
Znaki razreda:
[abc]: Ujema se s katerim koli od znakov a, b ali c.[^abc]: Ujema se s katerim koli znakom, ki *ni* a, b ali c.[a-z]: Ujema se s katero koli malo črko od a do z.[A-Z]: Ujema se s katero koli veliko črko od A do Z.[0-9]: Ujema se s katero koli števko od 0 do 9.[a-zA-Z0-9]: Ujema se s katerim koli alfanumeričnim znakom.\d: Ujema se s katero koli števko (enakovredno[0-9]).\D: Ujema se s katerim koli znakom, ki ni števka (enakovredno[^0-9]).\w: Ujema se s katerim koli besednim znakom (alfanumerični znaki in podčrtaj; enakovredno[a-zA-Z0-9_]).\W: Ujema se s katerim koli znakom, ki ni besedni (enakovredno[^a-zA-Z0-9_]).\s: Ujema se s katerim koli presledkom (presledek, tabulator, nova vrstica itd.).\S: Ujema se s katerim koli znakom, ki ni presledek.
Kvantifikatorji:
*: Ujema se s predhodnim elementom nič ali večkrat. Na primer,a*se ujema z "", "a", "aa", "aaa" in tako naprej.+: Ujema se s predhodnim elementom enkrat ali večkrat. Na primer,a+se ujema z "a", "aa", "aaa", ne pa z "".?: Ujema se s predhodnim elementom nič ali enkrat. Na primer,a?se ujema z "" ali "a".{n}: Ujema se s predhodnim elementom natančno *n*-krat. Na primer,a{3}se ujema z "aaa".{n,}: Ujema se s predhodnim elementom *n*-krat ali večkrat. Na primer,a{2,}se ujema z "aa", "aaa", "aaaa" in tako naprej.{n,m}: Ujema se s predhodnim elementom med *n*- in *m*-krat (vključno). Na primer,a{2,4}se ujema z "aa", "aaa" ali "aaaa".
Sidra:
^: Ujema se z začetkom niza. Na primer,^Hellose ujema z nizi, ki se *začnejo* s "Hello".$: Ujema se s koncem niza. Na primer,World$se ujema z nizi, ki se *končajo* z "World".\b: Ujema se z mejo besede. To je položaj med besednim znakom (\w) in ne-besednim znakom (\W) ali na začetku ali koncu niza. Na primer,\bword\bse ujema s celotno besedo "word".
Zastavice:
Zastavice regularnih izrazov spreminjajo njihovo obnašanje. Dodajo se na konec literala regularnega izraza ali se posredujejo kot drugi argument konstruktorju RegExp.
g(global): Ujema se z vsemi pojavitvami vzorca, ne le s prvo.i(ignore case): Izvede ujemanje neobčutljivo na velikost črk.m(multiline): Omogoči večvrstični način, kjer se^in$ujemata z začetkom in koncem vsake vrstice (ločene z\n).s(dotAll): Dovoljuje, da se pika (.) ujema tudi z znaki za novo vrstico.u(unicode): Omogoči polno podporo za Unicode.y(sticky): Ujema se samo od indeksa, ki ga določa lastnostlastIndexregularnega izraza.
Metode za regularne izraze v JavaScriptu
JavaScript ponuja več metod za delo z regularnimi izrazi:
test(): Preveri, ali se niz ujema z vzorcem. Vrnetruealifalse.exec(): Izvede iskanje ujemanja v nizu. Vrne polje, ki vsebuje ujemajoče se besedilo in zajete skupine, alinull, če ni ujemanja.match(): Vrne polje z rezultati ujemanja niza z regularnim izrazom. Obnaša se različno z in brez zastaviceg.search(): Preveri ujemanje v nizu. Vrne indeks prvega ujemanja ali -1, če ni ujemanja.replace(): Zamenja pojavitve vzorca z nadomestnim nizom ali funkcijo, ki vrne nadomestni niz.split(): Razdeli niz v polje podnizov na podlagi regularnega izraza.
Primeri uporabe metod regularnih izrazov:
// test()
const regex = /hello/;
const str = "hello world";
console.log(regex.test(str)); // Izhod: true
// exec()
const regex2 = /hello (\w+)/;
const str2 = "hello world";
const result = regex2.exec(str2);
console.log(result); // Izhod: ["hello world", "world", index: 0, input: "hello world", groups: undefined]
// match() z zastavico 'g'
const regex3 = /\d+/g; // Ujema eno ali več števk globalno
const str3 = "There are 123 apples and 456 oranges.";
const matches = str3.match(regex3);
console.log(matches); // Izhod: ["123", "456"]
// match() brez zastavice 'g'
const regex4 = /\d+/;
const str4 = "There are 123 apples and 456 oranges.";
const match = str4.match(regex4);
console.log(match); // Izhod: ["123", index: 11, input: "There are 123 apples and 456 oranges.", groups: undefined]
// search()
const regex5 = /world/;
const str5 = "hello world";
console.log(str5.search(regex5)); // Izhod: 6
// replace()
const regex6 = /world/;
const str6 = "hello world";
const newStr = str6.replace(regex6, "JavaScript");
console.log(newStr); // Izhod: hello JavaScript
// replace() s funkcijo
const regex7 = /(\d+)-(\d+)-(\d+)/;
const str7 = "Today's date is 2023-10-27";
const newStr2 = str7.replace(regex7, (match, year, month, day) => {
return `${day}/${month}/${year}`;
});
console.log(newStr2); // Izhod: Today's date is 27/10/2023
// split()
const regex8 = /, /;
const str8 = "apple, banana, cherry";
const arr = str8.split(regex8);
console.log(arr); // Izhod: ["apple", "banana", "cherry"]
Napredne tehnike regularnih izrazov
Skupine za zajemanje:
Oklepaji () se uporabljajo za ustvarjanje skupin za zajemanje v regularnih izrazih. Zajete skupine omogočajo ekstrakcijo določenih delov ujemajočega se besedila. Metodi exec() in match() vrneta polje, kjer je prvi element celotno ujemanje, naslednji elementi pa so zajete skupine.
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match[0]); // Izhod: 2023-10-27 (Celotno ujemanje)
console.log(match[1]); // Izhod: 2023 (Prva zajeta skupina - leto)
console.log(match[2]); // Izhod: 10 (Druga zajeta skupina - mesec)
console.log(match[3]); // Izhod: 27 (Tretja zajeta skupina - dan)
Poimenovane skupine za zajemanje:
ES2018 je uvedel poimenovane skupine za zajemanje, ki omogočajo dodeljevanje imen skupinam za zajemanje s sintakso (?. To naredi kodo bolj berljivo in lažjo za vzdrževanje.
const regex = /(?\d{4})-(?\d{2})-(?\d{2})/;
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match.groups.year); // Izhod: 2023
console.log(match.groups.month); // Izhod: 10
console.log(match.groups.day); // Izhod: 27
Ne-zajemajoče skupine:
Če morate združiti dele regularnega izraza, ne da bi jih zajeli (npr. za uporabo kvantifikatorja na skupini), lahko uporabite ne-zajemajočo skupino s sintakso (?:...). S tem se izognete nepotrebni alokaciji pomnilnika za zajete skupine.
const regex = /(?:https?:\/\/)?([\w\.]+)/; // Ujema se z URL-jem, vendar zajame samo ime domene
const url = "https://www.example.com/path";
const match = regex.exec(url);
console.log(match[1]); // Izhod: www.example.com
Lookarounds (Preverjanje okolice):
Lookarounds so trditve ničelne širine, ki se ujemajo s položajem v nizu na podlagi vzorca, ki je pred (lookbehind) ali za (lookahead) tem položajem, ne da bi vključili vzorec lookarounda v samo ujemanje.
- Pozitivni lookahead:
(?=...)Ujema se, če vzorec znotraj lookahead-a *sledi* trenutnemu položaju. - Negativni lookahead:
(?!...)Ujema se, če vzorec znotraj lookahead-a *ne sledi* trenutnemu položaju. - Pozitivni lookbehind:
(?<=...)Ujema se, če vzorec znotraj lookbehind-a *predhaja* trenutnemu položaju. - Negativni lookbehind:
(? Ujema se, če vzorec znotraj lookbehind-a *ne predhaja* trenutnemu položaju.
Primer:
// Pozitivni lookahead: Pridobi ceno samo, če ji sledi USD
const regex = /\d+(?= USD)/;
const text = "The price is 100 USD";
const match = text.match(regex);
console.log(match); // Izhod: ["100"]
// Negativni lookahead: Pridobi besedo samo, če ji ne sledi številka
const regex2 = /\b\w+\b(?! \d)/;
const text2 = "apple 123 banana orange 456";
const matches = text2.match(regex2);
console.log(matches); // Izhod: null, ker match() vrne samo prvo ujemanje brez zastavice 'g', kar ni tisto, kar potrebujemo.
// za popravek:
const regex3 = /\b\w+\b(?! \d)/g;
const text3 = "apple 123 banana orange 456";
const matches3 = text3.match(regex3);
console.log(matches3); // Izhod: [ 'banana' ]
// Pozitivni lookbehind: Pridobi vrednost samo, če ji predhaja $
const regex4 = /(?<=\$)\d+/;
const text4 = "The price is $200";
const match4 = text4.match(regex4);
console.log(match4); // Izhod: ["200"]
// Negativni lookbehind: Pridobi besedo samo, če ji ne predhaja beseda 'not'
const regex5 = /(?
Povratne reference (Backreferences):
Povratne reference omogočajo sklicevanje na predhodno zajete skupine znotraj istega regularnega izraza. Uporabljajo sintakso \1, \2 itd., kjer številka ustreza številki zajete skupine.
const regex = /([a-z]+) \1/;
const text = "hello hello world";
const match = regex.exec(text);
console.log(match); // Izhod: ["hello hello", "hello", index: 0, input: "hello hello world", groups: undefined]
Praktična uporaba regularnih izrazov
Validacija e-poštnih naslovov:
Pogost primer uporabe regularnih izrazov je validacija e-poštnih naslovov. Čeprav je popoln regex za validacijo e-pošte izjemno kompleksen, je tukaj poenostavljen primer:
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
console.log(emailRegex.test("test@example.com")); // Izhod: true
console.log(emailRegex.test("invalid-email")); // Izhod: false
console.log(emailRegex.test("test@sub.example.co.uk")); // Izhod: true
Ekstrakcija URL-jev iz besedila:
Z regularnimi izrazi lahko izvlečete URL-je iz bloka besedila:
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
const text = "Visit our website at https://www.example.com or check out http://blog.example.org.";
const urls = text.match(urlRegex);
console.log(urls); // Izhod: ["https://www.example.com", "http://blog.example.org"]
Razčlenjevanje podatkov CSV:
Regularni izrazi se lahko uporabijo za razčlenjevanje podatkov CSV (Comma-Separated Values). Tukaj je primer razdelitve niza CSV v polje vrednosti, ki upošteva polja v narekovajih:
const csvString = 'John,Doe,"123, Main St",New York';
const csvRegex = /(?:"([^"]*(?:""[^"]*)*)")|([^,]+)/g; //Popravljen regex za CSV
let values = [];
let match;
while (match = csvRegex.exec(csvString)) {
values.push(match[1] ? match[1].replace(/""/g, '"') : match[2]);
}
console.log(values); // Izhod: ["John", "Doe", "123, Main St", "New York"]
Validacija mednarodnih telefonskih številk
Validacija mednarodnih telefonskih številk je kompleksna zaradi različnih formatov in dolžin. Zanesljiva rešitev pogosto vključuje uporabo knjižnice, vendar lahko poenostavljen regex zagotovi osnovno validacijo:
const phoneRegex = /^\+(?:[0-9] ?){6,14}[0-9]$/;
console.log(phoneRegex.test("+1 555 123 4567")); // Izhod: true (primer ZDA)
console.log(phoneRegex.test("+44 20 7946 0500")); // Izhod: true (primer ZK)
console.log(phoneRegex.test("+81 3 3224 5000")); // Izhod: true (primer Japonska)
console.log(phoneRegex.test("123-456-7890")); // Izhod: false
Validacija moči gesla
Regularni izrazi so uporabni za uveljavljanje pravilnikov o moči gesla. Spodnji primer preverja minimalno dolžino, velike in male črke ter številko.
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
console.log(passwordRegex.test("P@ssword123")); // Izhod: true
console.log(passwordRegex.test("password")); // Izhod: false (brez velike črke ali številke)
console.log(passwordRegex.test("Password")); // Izhod: false (brez številke)
console.log(passwordRegex.test("Pass123")); // Izhod: false (brez male črke)
console.log(passwordRegex.test("P@ss1")); // Izhod: false (manj kot 8 znakov)
Tehnike za optimizacijo regularnih izrazov
Regularni izrazi so lahko računsko zahtevni, še posebej pri kompleksnih vzorcih ali velikih vnosih. Tukaj je nekaj tehnik za optimizacijo delovanja regularnih izrazov:
- Bodite specifični: Izogibajte se uporabi preveč splošnih vzorcev, ki se lahko ujemajo z več, kot je predvideno.
- Uporabite sidra: Kadarkoli je mogoče, zasidrajte regex na začetek ali konec niza (
^,$). - Izogibajte se povratnemu sledenju (Backtracking): Zmanjšajte povratno sledenje z uporabo posesivnih kvantifikatorjev (npr.
++namesto+) ali atomskih skupin ((?>...)), kjer je to primerno. - Prevedite enkrat: Če isti regex uporabljate večkrat, ga prevedite enkrat in ponovno uporabite objekt
RegExp. - Pametno uporabljajte razrede znakov: Razredi znakov (
[]) so na splošno hitrejši od alternacij (|). - Ohranite preprostost: Izogibajte se preveč kompleksnim regularnim izrazom, ki jih je težko razumeti in vzdrževati. Včasih je razdelitev kompleksne naloge na več preprostejših regularnih izrazov ali uporaba drugih tehnik za manipulacijo nizov lahko bolj učinkovita.
Pogoste napake pri regularnih izrazih
- Pozabljanje na ubežanje metaznakov: Neuspešno ubežanje posebnih znakov, kot so
.,*,+,?,$,^,(,),[,],{,},|in\, ko želite, da se ujemajo dobesedno. - Prekomerna uporaba
.(pike): Pika se ujema s katerim koli znakom (razen nove vrstice v nekaterih načinih), kar lahko povzroči nepričakovana ujemanja, če je ne uporabljate previdno. Bodite bolj specifični, kjer je to mogoče, z uporabo razredov znakov ali drugih bolj omejujočih vzorcev. - Pohlepnost (Greediness): Privzeto so kvantifikatorji, kot sta
*in+, pohlepni in se bodo ujemali z največjim možnim delom niza. Uporabite lene kvantifikatorje (*?,+?), ko se morate ujemati z najkrajšim možnim nizom. - Nepravilna uporaba sider: Napačno razumevanje obnašanja
^(začetek niza/vrstice) in$(konec niza/vrstice) lahko vodi do napačnega ujemanja. Ne pozabite uporabiti zastavicem(večvrstično), ko delate z večvrstičnimi nizi in želite, da se^in$ujemata z začetkom in koncem vsake vrstice. - Neupoštevanje robnih primerov: Neupoštevanje vseh možnih vhodnih scenarijev in robnih primerov lahko povzroči hrošče. Temeljito preizkusite svoje regularne izraze z različnimi vnosi, vključno s praznimi nizi, neveljavnimi znaki in mejnimi pogoji.
- Težave z zmogljivostjo: Ustvarjanje preveč kompleksnih in neučinkovitih regularnih izrazov lahko povzroči težave z zmogljivostjo, še posebej pri velikih vnosih. Optimizirajte svoje regularne izraze z uporabo bolj specifičnih vzorcev, izogibanjem nepotrebnemu povratnemu sledenju in prevajanjem regularnih izrazov, ki se uporabljajo večkrat.
- Ignoriranje kodiranja znakov: Nepravilno ravnanje s kodiranjem znakov (še posebej Unicode) lahko povzroči nepričakovane rezultate. Uporabite zastavico
u, ko delate z znaki Unicode, da zagotovite pravilno ujemanje.
Zaključek
Regularni izrazi so dragoceno orodje za ujemanje vzorcev in manipulacijo besedila v JavaScriptu. Obvladovanje sintakse in tehnik regularnih izrazov vam omogoča učinkovito reševanje širokega spektra problemov, od validacije podatkov do kompleksne obdelave besedil. Z razumevanjem konceptov, obravnavanih v tem vodniku, in z vajo na primerih iz resničnega sveta lahko postanete spretni pri uporabi regularnih izrazov za izboljšanje svojih razvijalskih veščin v JavaScriptu.
Ne pozabite, da so lahko regularni izrazi kompleksni, in pogosto je koristno, da jih temeljito preizkusite z uporabo spletnih preizkuševalcev regularnih izrazov, kot sta regex101.com ali regexr.com. To vam omogoča vizualizacijo ujemanj in učinkovito odpravljanje morebitnih težav. Veselo kodiranje!