Explorați potrivirea avansată a modelelor în JavaScript folosind expresii regulate. Aflați despre sintaxa regex, aplicații practice și tehnici de optimizare pentru un cod eficient și robust.
Potrivirea modelelor în JavaScript cu expresii regulate: Un ghid cuprinzător
Expresiile regulate (regex) sunt un instrument puternic pentru potrivirea modelelor și manipularea textului în JavaScript. Acestea permit dezvoltatorilor să caute, să valideze și să transforme șiruri de caractere pe baza unor modele definite. Acest ghid oferă o privire de ansamblu cuprinzătoare asupra expresiilor regulate în JavaScript, acoperind sintaxa, utilizarea și tehnicile avansate.
Ce sunt expresiile regulate?
O expresie regulată este o secvență de caractere care definește un model de căutare. Aceste modele sunt folosite pentru a potrivi și manipula șiruri de caractere. Expresiile regulate sunt utilizate pe scară largă în programare pentru sarcini precum:
- Validarea datelor: Asigurarea conformității datelor introduse de utilizator cu formate specifice (de ex., adrese de e-mail, numere de telefon).
- Extragerea datelor: Preluarea informațiilor specifice din text (de ex., extragerea datelor calendaristice, URL-urilor sau prețurilor).
- Căutare și înlocuire: Găsirea și înlocuirea textului pe baza unor modele complexe.
- Procesarea textului: Împărțirea, unirea sau transformarea șirurilor de caractere pe baza unor reguli definite.
Crearea expresiilor regulate în JavaScript
În JavaScript, expresiile regulate pot fi create în două moduri:
- Folosind un literal de expresie regulată: Încadrați modelul între bare oblice (
/). - Folosind constructorul
RegExp: Creați un obiectRegExpcu modelul ca șir de caractere.
Exemplu:
// Folosind un literal de expresie regulată
const regexLiteral = /hello/;
// Folosind constructorul RegExp
const regexConstructor = new RegExp("hello");
Alegerea între cele două metode depinde de faptul dacă modelul este cunoscut la momentul compilării sau generat dinamic. Folosiți notația literală atunci când modelul este fix și cunoscut în avans. Folosiți constructorul atunci când modelul trebuie construit programatic, în special atunci când se încorporează variabile.
Sintaxa de bază Regex
Expresiile regulate constau din caractere care reprezintă modelul ce trebuie potrivit. Iată câteva componente fundamentale ale regex:
- Caractere literale: Potrivesc caracterele în sine (de ex.,
/a/potrivește caracterul 'a'). - Metacaractere: Au semnificații speciale (de ex.,
.,^,$,*,+,?,[],{},(),\,|). - Clase de caractere: Reprezintă seturi de caractere (de ex.,
[abc]potrivește 'a', 'b' sau 'c'). - Cuantificatori: Specifică de câte ori trebuie să apară un caracter sau un grup (de ex.,
*,+,?,{n},{n,},{n,m}). - Ancore: Potrivesc poziții în șirul de caractere (de ex.,
^potrivește începutul,$potrivește sfârșitul).
Metacaractere comune:
.(punct): Potrivește orice caracter unic, cu excepția caracterului de linie nouă.^(caret): Potrivește începutul șirului de caractere.$(dolar): Potrivește sfârșitul șirului de caractere.*(asterisk): Potrivește zero sau mai multe apariții ale caracterului sau grupului precedent.+(plus): Potrivește una sau mai multe apariții ale caracterului sau grupului precedent.?(semn de întrebare): Potrivește zero sau o apariție a caracterului sau grupului precedent. Folosit pentru caractere opționale.[](paranteze drepte): Definește o clasă de caractere, potrivind orice caracter unic din interiorul parantezelor.{}(acolade): Specifică numărul de apariții de potrivit.{n}potrivește exact de n ori,{n,}potrivește de n sau de mai multe ori,{n,m}potrivește între n și m ori.()(paranteze rotunde): Grupează caracterele și capturează subșirul potrivit.\(backslash): Escapează metacaracterele, permițându-vă să le potriviți literal.|(pipe): Acționează ca un operator "sau", potrivind fie expresia dinainte, fie cea de după el.
Clase de caractere:
[abc]: Potrivește oricare dintre caracterele a, b sau c.[^abc]: Potrivește orice caracter care *nu* este a, b sau c.[a-z]: Potrivește orice literă mică de la a la z.[A-Z]: Potrivește orice literă mare de la A la Z.[0-9]: Potrivește orice cifră de la 0 la 9.[a-zA-Z0-9]: Potrivește orice caracter alfanumeric.\d: Potrivește orice cifră (echivalent cu[0-9]).\D: Potrivește orice caracter non-cifră (echivalent cu[^0-9]).\w: Potrivește orice caracter de cuvânt (alfanumeric plus underscore; echivalent cu[a-zA-Z0-9_]).\W: Potrivește orice caracter non-cuvânt (echivalent cu[^a-zA-Z0-9_]).\s: Potrivește orice caracter de spațiu alb (spațiu, tab, linie nouă, etc.).\S: Potrivește orice caracter non-spațiu alb.
Cuantificatori:
*: Potrivește elementul precedent de zero sau mai multe ori. De exemplu,a*potrivește "", "a", "aa", "aaa" și așa mai departe.+: Potrivește elementul precedent de una sau mai multe ori. De exemplu,a+potrivește "a", "aa", "aaa", dar nu "".?: Potrivește elementul precedent de zero sau o dată. De exemplu,a?potrivește "" sau "a".{n}: Potrivește elementul precedent exact de *n* ori. De exemplu,a{3}potrivește "aaa".{n,}: Potrivește elementul precedent de *n* sau mai multe ori. De exemplu,a{2,}potrivește "aa", "aaa", "aaaa" și așa mai departe.{n,m}: Potrivește elementul precedent între *n* și *m* ori (inclusiv). De exemplu,a{2,4}potrivește "aa", "aaa" sau "aaaa".
Ancore:
^: Potrivește începutul șirului de caractere. De exemplu,^Hellopotrivește șirurile care *încep* cu "Hello".$: Potrivește sfârșitul șirului de caractere. De exemplu,World$potrivește șirurile care *se termină* cu "World".\b: Potrivește o limită de cuvânt. Aceasta este poziția dintre un caracter de cuvânt (\w) și un caracter non-cuvânt (\W) sau începutul sau sfârșitul șirului. De exemplu,\bword\bpotrivește cuvântul întreg "word".
Steaguri (Flags):
Steagurile Regex modifică comportamentul expresiilor regulate. Acestea sunt adăugate la sfârșitul literalului regex sau transmise ca al doilea argument la constructorul RegExp.
g(global): Potrivește toate aparițiile modelului, nu doar prima.i(ignore case): Efectuează potrivirea fără a ține cont de majuscule/minuscule.m(multiline): Activează modul multilinie, unde^și$potrivesc începutul și sfârșitul fiecărei linii (separate prin\n).s(dotAll): Permite punctului (.) să potrivească și caracterele de linie nouă.u(unicode): Activează suportul complet pentru Unicode.y(sticky): Potrivește doar de la indexul indicat de proprietatealastIndexa regex-ului.
Metode Regex în JavaScript
JavaScript oferă mai multe metode pentru a lucra cu expresii regulate:
test(): Testează dacă un șir de caractere se potrivește cu modelul. Returneazătruesaufalse.exec(): Execută o căutare pentru o potrivire într-un șir de caractere. Returnează un tablou care conține textul potrivit și grupurile capturate, saunulldacă nu se găsește nicio potrivire.match(): Returnează un tablou care conține rezultatele potrivirii unui șir de caractere cu o expresie regulată. Se comportă diferit cu și fără steagulg.search(): Testează pentru o potrivire într-un șir de caractere. Returnează indexul primei potriviri, sau -1 dacă nu se găsește nicio potrivire.replace(): Înlocuiește aparițiile unui model cu un șir de înlocuire sau cu o funcție care returnează șirul de înlocuire.split(): Împarte un șir de caractere într-un tablou de subșiruri pe baza unei expresii regulate.
Exemple de utilizare a metodelor Regex:
// test()
const regex = /hello/;
const str = "hello world";
console.log(regex.test(str)); // Rezultat: true
// exec()
const regex2 = /hello (\w+)/;
const str2 = "hello world";
const result = regex2.exec(str2);
console.log(result); // Rezultat: ["hello world", "world", index: 0, input: "hello world", groups: undefined]
// match() cu steagul 'g'
const regex3 = /\d+/g; // Potrivește una sau mai multe cifre global
const str3 = "There are 123 apples and 456 oranges.";
const matches = str3.match(regex3);
console.log(matches); // Rezultat: ["123", "456"]
// match() fără steagul 'g'
const regex4 = /\d+/;
const str4 = "There are 123 apples and 456 oranges.";
const match = str4.match(regex4);
console.log(match); // Rezultat: ["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)); // Rezultat: 6
// replace()
const regex6 = /world/;
const str6 = "hello world";
const newStr = str6.replace(regex6, "JavaScript");
console.log(newStr); // Rezultat: hello JavaScript
// replace() cu o funcție
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); // Rezultat: Today's date is 27/10/2023
// split()
const regex8 = /, /;
const str8 = "apple, banana, cherry";
const arr = str8.split(regex8);
console.log(arr); // Rezultat: ["apple", "banana", "cherry"]
Tehnici Regex avansate
Grupuri de capturare:
Parantezele () sunt folosite pentru a crea grupuri de capturare în expresiile regulate. Grupurile capturate vă permit să extrageți părți specifice ale textului potrivit. Metodele exec() și match() returnează un tablou unde primul element este potrivirea completă, iar elementele ulterioare sunt grupurile capturate.
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match[0]); // Rezultat: 2023-10-27 (Potrivirea completă)
console.log(match[1]); // Rezultat: 2023 (Primul grup capturat - anul)
console.log(match[2]); // Rezultat: 10 (Al doilea grup capturat - luna)
console.log(match[3]); // Rezultat: 27 (Al treilea grup capturat - ziua)
Grupuri de capturare denumite:
ES2018 a introdus grupurile de capturare denumite, care vă permit să atribuiți nume grupurilor de capturare folosind sintaxa (?. Acest lucru face codul mai lizibil și mai ușor de întreținut.
const regex = /(?\d{4})-(?\d{2})-(?\d{2})/;
const dateString = "2023-10-27";
const match = regex.exec(dateString);
console.log(match.groups.year); // Rezultat: 2023
console.log(match.groups.month); // Rezultat: 10
console.log(match.groups.day); // Rezultat: 27
Grupuri non-capturante:
Dacă trebuie să grupați părți ale unui regex fără a le captura (de ex., pentru a aplica un cuantificator unui grup), puteți folosi un grup non-capturant cu sintaxa (?:...). Acest lucru evită alocarea inutilă de memorie pentru grupurile capturate.
const regex = /(?:https?:\/\/)?([\w\.]+)/; // Potrivește un URL dar capturează doar numele domeniului
const url = "https://www.example.com/path";
const match = regex.exec(url);
console.log(match[1]); // Rezultat: www.example.com
Lookarounds (aserțiuni contextuale):
Lookarounds sunt aserțiuni de lățime zero care potrivesc o poziție într-un șir de caractere pe baza unui model care precede (lookbehind) sau urmează (lookahead) acea poziție, fără a include modelul lookaround în potrivirea însăși.
- Lookahead pozitiv:
(?=...)Potrivește dacă modelul din interiorul lookahead-ului *urmează* poziția curentă. - Lookahead negativ:
(?!...)Potrivește dacă modelul din interiorul lookahead-ului *nu* urmează poziția curentă. - Lookbehind pozitiv:
(?<=...)Potrivește dacă modelul din interiorul lookbehind-ului *precede* poziția curentă. - Lookbehind negativ:
(? Potrivește dacă modelul din interiorul lookbehind-ului *nu* precede poziția curentă.
Exemplu:
// Lookahead pozitiv: Obține prețul doar când este urmat de USD
const regex = /\d+(?= USD)/;
const text = "The price is 100 USD";
const match = text.match(regex);
console.log(match); // Rezultat: ["100"]
// Lookahead negativ: Obține cuvântul doar când nu este urmat de un număr
const regex2 = /\b\w+\b(?! \d)/;
const text2 = "apple 123 banana orange 456";
const matches = text2.match(regex2);
console.log(matches); // Rezultat: null deoarece match() returnează doar prima potrivire fără steagul 'g', ceea ce nu ne trebuie.
// pentru a repara:
const regex3 = /\b\w+\b(?! \d)/g;
const text3 = "apple 123 banana orange 456";
const matches3 = text3.match(regex3);
console.log(matches3); // Rezultat: [ 'banana' ]
// Lookbehind pozitiv: Obține valoarea doar când este precedată de $
const regex4 = /(?<=\$)\d+/;
const text4 = "The price is $200";
const match4 = text4.match(regex4);
console.log(match4); // Rezultat: ["200"]
// Lookbehind negativ: Obține cuvântul doar când nu este precedat de cuvântul 'not'
const regex5 = /(?
Referințe inverse (Backreferences):
Referințele inverse vă permit să faceți referire la grupurile capturate anterior în cadrul aceleiași expresii regulate. Acestea folosesc sintaxa \1, \2, etc., unde numărul corespunde numărului grupului capturat.
const regex = /([a-z]+) \1/;
const text = "hello hello world";
const match = regex.exec(text);
console.log(match); // Rezultat: ["hello hello", "hello", index: 0, input: "hello hello world", groups: undefined]
Aplicații practice ale expresiilor regulate
Validarea adreselor de e-mail:
Un caz de utilizare comun pentru expresiile regulate este validarea adreselor de e-mail. Deși un regex perfect pentru validarea e-mailurilor este extrem de complex, iată un exemplu simplificat:
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
console.log(emailRegex.test("test@example.com")); // Rezultat: true
console.log(emailRegex.test("invalid-email")); // Rezultat: false
console.log(emailRegex.test("test@sub.example.co.uk")); // Rezultat: true
Extragerea URL-urilor din text:
Puteți folosi expresii regulate pentru a extrage URL-uri dintr-un bloc de text:
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); // Rezultat: ["https://www.example.com", "http://blog.example.org"]
Parsarea datelor CSV:
Expresiile regulate pot fi folosite pentru a parsa date CSV (Comma-Separated Values). Iată un exemplu de împărțire a unui șir CSV într-un tablou de valori, gestionând câmpurile între ghilimele:
const csvString = 'John,Doe,"123, Main St",New York';
const csvRegex = /(?:"([^"]*(?:""[^"]*)*)")|([^,]+)/g; //Regex CSV corectat
let values = [];
let match;
while (match = csvRegex.exec(csvString)) {
values.push(match[1] ? match[1].replace(/""/g, '"') : match[2]);
}
console.log(values); // Rezultat: ["John", "Doe", "123, Main St", "New York"]
Validarea numerelor de telefon internaționale
Validarea numerelor de telefon internaționale este complexă din cauza formatelor și lungimilor variate. O soluție robustă implică adesea utilizarea unei biblioteci, dar un regex simplificat poate oferi o validare de bază:
const phoneRegex = /^\+(?:[0-9] ?){6,14}[0-9]$/;
console.log(phoneRegex.test("+1 555 123 4567")); // Rezultat: true (Exemplu SUA)
console.log(phoneRegex.test("+44 20 7946 0500")); // Rezultat: true (Exemplu UK)
console.log(phoneRegex.test("+81 3 3224 5000")); // Rezultat: true (Exemplu Japonia)
console.log(phoneRegex.test("123-456-7890")); // Rezultat: false
Validarea puterii parolei
Expresiile regulate sunt utile pentru a impune politici de putere a parolei. Exemplul de mai jos verifică lungimea minimă, litere mari, litere mici și o cifră.
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/;
console.log(passwordRegex.test("P@ssword123")); // Rezultat: true
console.log(passwordRegex.test("password")); // Rezultat: false (fără majusculă sau cifră)
console.log(passwordRegex.test("Password")); // Rezultat: false (fără cifră)
console.log(passwordRegex.test("Pass123")); // Rezultat: false (fără minusculă)
console.log(passwordRegex.test("P@ss1")); // Rezultat: false (mai puțin de 8 caractere)
Tehnici de optimizare Regex
Expresiile regulate pot fi costisitoare din punct de vedere computațional, în special pentru modele complexe sau intrări mari. Iată câteva tehnici pentru optimizarea performanței regex:
- Fiți specifici: Evitați utilizarea modelelor prea generale care pot potrivi mai mult decât este intenționat.
- Folosiți ancore: Ancorați regex-ul la începutul sau sfârșitul șirului de caractere ori de câte ori este posibil (
^,$). - Evitați backtracking-ul: Minimizați backtracking-ul folosind cuantificatori posesivi (de ex.,
++în loc de+) sau grupuri atomice ((?>...)) atunci când este cazul. - Compilați o singură dată: Dacă folosiți același regex de mai multe ori, compilați-l o dată și reutilizați obiectul
RegExp. - Folosiți clasele de caractere cu înțelepciune: Clasele de caractere (
[]) sunt în general mai rapide decât alternările (|). - Păstrați simplitatea: Evitați regex-urile prea complexe, greu de înțeles și de întreținut. Uneori, împărțirea unei sarcini complexe în mai multe regex-uri mai simple sau utilizarea altor tehnici de manipulare a șirurilor de caractere poate fi mai eficientă.
Greșeli comune în Regex
- Uitați să escape-ați metacaracterele: Nerespectarea escape-ului caracterelor speciale precum
.,*,+,?,$,^,(,),[,],{,},|și\atunci când doriți să le potriviți literal. - Folosirea excesivă a
.(punct): Punctul potrivește orice caracter (cu excepția liniei noi în unele moduri), ceea ce poate duce la potriviri neașteptate dacă nu este folosit cu atenție. Fiți mai specifici când este posibil, folosind clase de caractere sau alte modele mai restrictive. - Lăcomia (Greediness): În mod implicit, cuantificatorii precum
*și+sunt lacomi și vor potrivi cât mai mult posibil. Folosiți cuantificatori leneși (*?,+?) atunci când trebuie să potriviți cel mai scurt șir posibil. - Folosirea incorectă a ancorelor: Neînțelegerea comportamentului
^(începutul șirului/liniei) și$(sfârșitul șirului/liniei) poate duce la potriviri incorecte. Nu uitați să folosiți steagulm(multiline) atunci când lucrați cu șiruri de caractere pe mai multe linii și doriți ca^și$să potrivească începutul și sfârșitul fiecărei linii. - Neglijarea cazurilor extreme (edge cases): Neluarea în considerare a tuturor scenariilor posibile de intrare și a cazurilor extreme poate duce la erori. Testați-vă regex-urile amănunțit cu o varietate de intrări, inclusiv șiruri goale, caractere invalide și condiții de limită.
- Probleme de performanță: Construirea unor regex-uri prea complexe și ineficiente poate cauza probleme de performanță, în special cu intrări mari. Optimizați-vă regex-urile folosind modele mai specifice, evitând backtracking-ul inutil și compilând regex-urile care sunt folosite în mod repetat.
- Ignorarea codificării caracterelor: Negestionarea corectă a codificărilor de caractere (în special Unicode) poate duce la rezultate neașteptate. Folosiți steagul
uatunci când lucrați cu caractere Unicode pentru a asigura o potrivire corectă.
Concluzie
Expresiile regulate sunt un instrument valoros pentru potrivirea modelelor și manipularea textului în JavaScript. Stăpânirea sintaxei și tehnicilor regex vă permite să rezolvați eficient o gamă largă de probleme, de la validarea datelor la procesarea complexă a textului. Înțelegând conceptele discutate în acest ghid și exersând cu exemple din lumea reală, puteți deveni proficient în utilizarea expresiilor regulate pentru a vă îmbunătăți abilitățile de dezvoltare JavaScript.
Rețineți că expresiile regulate pot fi complexe și este adesea util să le testați amănunțit folosind testere regex online precum regex101.com sau regexr.com. Acest lucru vă permite să vizualizați potrivirile și să depanați eficient orice problemă. Spor la codat!