Scrierea expresiilor regulate. Expresii regulate pentru începători

Am decis să scriu o foaie de cheat pe expresiile regulate. Poate într-o zi le voi uita. În plus, această postare poate fi considerată o continuare a seriei mele de tutoriale Perl.

1. Introducere

Câteva cuvinte pentru cei care nu prea știu despre ce vorbim despre care vorbim. Ați văzut vreodată măști de nume de fișiere - tot felul de *.html, nume de fișier.(txt|csv), etc.? Deci, expresiile regulate sunt aceleași „măști”, doar mai complexe. ÎN în mâini capabile expresiile regulate pot fi un instrument incredibil de puternic. Într-un fel sau altul, sunt folosite în 95% din scripturile mele.

Mulți oameni, nu în mod nerezonabil, cred că expresiile regulate sunt mai mult un limbaj de programare independent decât o parte a oricărui limbaj. Expresii regulate disponibil în Perl, PHP, Python, JavaScript, fișierele de configurare Apache... În funcție de limbaj, pot exista mici diferențe în sintaxa expresiei regulate, dar ideile de bază sunt aceleași.

Prin urmare, în ciuda faptului că toate exemplele din notă sunt scrise în Perl, informațiile furnizate vor fi utile și programatorilor care folosesc orice alt limbaj în munca lor. De exemplu, acest cod în PHP:

if (preg_match ("//" , $text ) ) (
// textul conține numere
) altfel (
// nu există numere în text
}

si acesta in Perl:

dacă ($text =~ // ) (
# textul conține numere
) altfel (

}

face acelasi lucru. După cum ați putea ghici din comentariile din cod, există o verificare pentru a vedea dacă șirul $text conține cel puțin o cifră.

2. Exemple simple

Ca întotdeauna, vom învăța din exemple. Paranteze pătrateîn expresiile regulate înseamnă „unul dintre caracterele enumerate trebuie să fie aici”. De exemplu, expresia de mai sus se potrivește cu orice șir care conține cel puțin o cifră. Similar cu expresia se potrivește cu orice șir care conține cel puțin una dintre primele trei litere ale alfabetului latin. Pentru a reprezenta orice personaj, cu excepţia specificat, se utilizează intrarea [^abcdef], adică cu simbolul capacului imediat după paranteza pătrată de deschidere.

Să presupunem că trebuie să verificăm dacă un șir conține vreun caracter al alfabetului latin. Nu este în întregime convenabil să enumerați toate cele 26 de litere, nu-i așa? Mai ales pentru astfel de cazuri în expresiile regulate pe care le puteți folosi liniuță între paranteze drepte pentru a desemna un set ordonat de caractere. Expresie Orice șir care conține cel puțin o literă minusculă a alfabetului latin se va potrivi. Prin analogie, exemplul dat anterior cu numere poate fi scris mai pe scurt:

dacă ($text =~ // ) (
# textul conține numere
) altfel (
# nu există numere în text
}

Și încă câteva exemple:

dacă ($text =~ // ) (
# textul conține cifre și/sau litere mici
# potrivit: abc, ZZaZZ, ===17
# nu este potrivit: EPIC FAIL, @^*!@#
}

dacă ($text =~ /[^0-9]/ ) (
# textul conține alte caractere decât numere
# se potrivește: abc, 123abc456, 0x1111111111
# nu este potrivit: 123, 123456, 9999999999
}

dacă ($text =~ // ) (
# textul conține litere din alfabetul latin
# potrivit: ___Abba___, zyx
# nu este potrivit: 0123, ^_^
}

dacă ($text =~ // ) (
# textul conține numere și litere de la A la F
# potrivit: ***777***, DeadC0de, intel, 0_o
# nu este potrivit: Xor, wiki
}

Să complicăm sarcina. Acum trebuie să verificăm nu doar prezența sau absența anumitor caractere, ci și dacă șirul se potrivește cu un anumit format. Iată câteva exemple simple:

dacă ($text =~ /num=/ ) (
# potrivit: num=1, some_num=000, bebenum=2(&^*
# nu este potrivit: NUM=1, my_num=-1, num=abc
}

dacă ($text =~ / / ) {
# se potrivește:
#zzz zzz
#
# nu este potrivit:
#
#
}

Cititorul atent se va întreba ce este asta semnul plus este în ultima expresie regulată? Acest simbol înseamnă „unul sau mai multe caractere specificate înainte de acest plus”. Simbolul înseamnă aproape același lucru stea"din zero până la orice număr de caractere specificat înainte de asterisc.” De exemplu, expresia A+ va potrivi o secvență de unul sau mai multe caractere A și expresia * - orice număr de cifre, inclusiv niciuna.

Uneori, numărul de caractere trebuie specificat mai precis. Acest lucru se poate face folosind bretele crete. De exemplu, expresia {8} se potrivește cu orice succesiune de exact opt ​​cifre și cu expresia {3,8} — o secvență care conține de la 3 la 8 caractere ale alfabetului latin.

Este posibil ca numărul din a doua poziție să nu fie specificat. Adică expresia {3,} poate apărea și. Înseamnă „cel puțin trei litere mici ale alfabetului latin”. Expresie {0,} complet similar cu asteriscul și {1,} - plus. Expresie {0,1} poate fi scris mai pe scurt folosind semnul întrebării.

Exemplu (nu cel mai simplu, dar interesant):

dacă ($text =~ // ) {
# se potrivește:
#dfgd dfgdfg
#
# nu este potrivit:
#
#
}

Dacă acest exemplu îți face creierul să fiarbă, este timpul să exersezi puțin cu expresiile obișnuite scriind programe de testare. Altfel de la lecturi suplimentare vei avea o mizerie în cap. Dacă totul este clar până acum, să mergem mai departe.

3. Cum se rupe o bucată de linie?

Simbol linie verticală(alias „pipe” sau pur și simplu „stick”) înseamnă „sau” în expresiile regulate. De exemplu, expresia {20}|{25} se potrivește cu toate șirurile care conțin 20 de caractere latine sau 25 de numere la rând. De obicei, acest simbol este folosit împreună cu parantezele, conceput pentru a grupa părți ale unei expresii regulate. Exemplu:

dacă ($filename =~ /backup(19|20)(2)-(2)-(2)/) {
# potrivit: backup2011-04-01, backup1999-01-13
# nu este potrivit: backup1873-12-12, backup2101-07-07
}

Parantezele au o altă funcție. Cu ajutorul lor, puteți rupe bucăți din liniile corespunzătoare. În PHP, rezultatul este stocat în variabila specificată de al treilea argument al funcției preg_match. În Perl, potrivirile pentru prima, a doua... a 9-a pereche de paranteze sunt stocate în variabilele $1, $2,..., $9. Dar este mai convenabil să folosiți această construcție:

dacă (my ($y, $m, $d) =
$filename =~ /backup((4))-((2))-((2))/) {
imprimare;
}

Întrebarea este la ce număr să căutați o potrivire în tabloul returnat dacă expresia regulată conține cuibărit paranteze? Este simplu - potrivirile sunt returnate în aceeași ordine ca și parantezele de deschidere. Exemplu:

my $filename = „./dumps/backup2011-04-01.tgz”;
$filename =~ /backup((20|19)(2))-((2))-((2))/;
imprimați „$1, $2, $3, $4 \n";
# va afișa: 2011, 20, 04, 01

Uneori am dori să grupăm o parte dintr-o expresie, dar să nu o returnăm. Pentru a face acest lucru, imediat după paranteza de deschidere trebuie să scrieți succesiune de semn de întrebare și două puncte. Exemplu:

dacă (my ($y, $m, $d) =
$filename =~ /backup((?:20|19)(2))-((2))-((2))/) {
imprimare "an = $y, luna = $m, ziua = $d\n";
}

De asemenea, parantezele pot fi urmate de semnul întrebării, plus sau asterisc, care indică faptul că constructul din paranteze este opțional, trebuie repetat de 1+ ori sau, respectiv, de 0+ ori. Utilizarea acoladelor după paranteze este, de asemenea, acceptabilă.

4. Începutul și sfârșitul liniei

Este adesea util să indicați într-o expresie regulată unde ar trebui să înceapă și/sau să se termine un șir. Primul se face folosind simbolul capacului la începutul expresiei, al doilea - folosind semnul dolarului la sfârșitul. Exemple:

dacă ($text =~ /^*/ ) (
# text care începe cu o cifră zecimală
# potrivit: 3, 801403, 6543bebebe
# nu este potrivit: 0275, -123, abc11111
}

dacă ($text =~ /^0x(1,8)$/ ) (
# număr hexazecimalîn notație C
# potrivit: 0x5f3759df, 0xDEADBEEF
# nu este potrivit: 0x1234xxx, xxx0x5678, xxx0x9ABCxxx
}

Nu e greu, nu? Vă rugăm să rețineți că atunci când verificați câmpurile formularului web, argumentele funcției înainte de a le înlocui într-o interogare SQL etc. Neapărat ar trebui verificat toateșir, așa cum s-a făcut în ultima expresie regulată.

Nota: Dacă cineva este interesat de ce asta" numere magice» 0x5f3759df și 0xDEADBEEF, consultați Wikipedia.

5. Personaje speciale

Pe lângă caracterele speciale menționate mai sus, trebuie remarcat în mod deosebit punct. Ea înseamnă orice alt caracter decât noua linie. Exemplu de utilizare:

dacă (meu ($numele ) = $arg =~ /^--numele=(.+)$/ ) (
tipăriți „Bună ziua, $name! \n";
}

În mod implicit, expresiile regulate produc ceea ce se numește parsing lacom. Cu alte cuvinte, se caută potriviri de lungime maximă. Când folosim un punct, acest lucru poate cauza probleme. De exemplu, trebuie să scoatem un text din sute de pagini HTML cu aproximativ următorul conținut:

<interval > Text<ei > text</em> text</span> Sursa: http://site/</span>

Următorul cod nu va returna ceea ce ne-am dori:

# expresia regulată conține o bară oblică, deci
# trebuie să folosească un alt delimitator
(.*)#;
print $text ;
# va imprima cea mai lungă potrivire:
#Text text textSource: http://site/

Iată ce se întâmplă dacă dezactivați analiza lacomă (rețineți semnul întrebării):

my ($text ) = $date =~ m # (.*?)#;
print $text ;
# va imprima prima potrivire:
#Text text text

Da, următoarele rânduri face acelasi lucru:

# intrare obișnuită...
$text =~ /({4})-({2})-({2})/ ;
# este de fapt doar o prescurtare pentru operatorul m//
$text =~ m/((4))-((2))-((2))/;
# în loc de bară oblică, puteți folosi diferite paranteze:
$text =~ m ( ([ 0 - 9 ] ( 4 ) ) - ( [ 0 - 9 ] ( 2 ) ) - ( [ 0 - 9 ] ( 2 ) ) ) ;
$text =~ m< ([ 0 - 9 ] { 4 } ) - ([ 0 - 9 ] { 2 } ) - ([ 0 - 9 ] { 2 } ) >;
$text =~ m [ ([ 0 - 9 ] ( 4 ) ) - ( [ 0 - 9 ] ( 2 ) ) - ( [ 0 - 9 ] ( 2 ) ) ] ;
$text =~ m (([ 0 - 9 ] ( 4 ) ) - ( [ 0 - 9 ] ( 2 ) ) - ( [ 0 - 9 ] ( 2 ) ) ) ;
# sau chiar simboluri ca acesta:
$text =~ m ! ([ 0 - 9 ] ( 4 ) ) - ( [ 0 - 9 ] ( 2 ) ) - ( [ 0 - 9 ] ( 2 ) ) !;
$text =~ m | ([ 0 - 9 ] ( 4 ) ) - ( [ 0 - 9 ] ( 2 ) ) - ( [ 0 - 9 ] ( 2 ) ) |;
$text =~ m #({4})-({2})-({2})#;
# precum și cap, ghilimele, două puncte, virgulă, punct, ...

De ce au fost necesare atât de multe moduri de a scrie expresii regulate? Imaginați-vă că expresia conține bare oblice, puncte, virgule și alte simboluri, dar nu conține un semn de exclamare. Apoi, evident, nu putem folosi bare oblice, puncte și așa mai departe pentru a indica începutul și sfârșitul unei expresii regulate, dar semnul exclamării- Can.

Adesea, în expresiile regulate, trebuie să le folosiți backslash. Plasat înainte de punct, paranteză, plus, cap și alte simboluri, înseamnă „următorul simbol înseamnă exact simbolulși nu altceva.” De exemplu, iată cum să determinați o extensie de fișier după numele său:

# bară oblică inversă punct scapat
# înseamnă un punct, nu „orice caracter”
my ($ext ) = $fname =~ /\.(+)$/ ;
imprimare "nume fișier: $fname, extensie: $ext\n";

În plus, bara oblică inversă este utilizată în următoarea notație:

  • \t— denotă un caracter tabulator ( t ab)
  • \rŞi \n- caractere de întoarcere la cărucior ( rîntoarcere) și linie nouă ( n linie nouă)
  • \xNN— potrivește un caracter cu codul ASCII NN, de exemplu \x41 corespunde majusculei A a alfabetului latin
  • \s- se potrivește cu un spațiu ( s ritm), filă, linie nouă sau întoarcere la cărucior
  • \d- înseamnă orice număr ( d igit), sau mai precis, ceea ce este considerat un număr în Unicode (vezi diapozitivul numărul 102 din această prezentare)
  • \w- înseamnă așa-numitul „cuvânt” ( w ord), analog

În ultimele trei expresii, scrierea literei cu litere mari înseamnă negație. De exemplu, \D corespunde expresiei [^0-9] , \W- expresie [^0-9a-zA-Z_], A \S- orice caracter „non-spațiu alb”.

Toate aceste expresii „litera” pot fi folosite în interior paranteze pătrate. De exemplu, expresia complet echivalent .

Expresiile merită o atenție deosebită \bŞi \B, adică limita de cuvinte(în același sens de „cuvânt” ca și în cazul lui \w) și, respectiv, absența unei granițe de cuvânt. De exemplu, expresia perl\b se potrivește cu șirul „perl rulez!”, dar nu se potrivește cu „perlmonk”. Cu expresie perl\B totul este exact invers. Sper că ideea este clară.

Si inca un exemplu:

#rupere Numele complet fișier la cale și nume
my ($cale , $fname ) = $nume_complet =~ /^(.*)\/([^\/]+)$/ ;

Ea ilustrează utilizarea backslash pentru a scăpa de un caracter care este folosit pentru a desemna limitele expresiilor regulate. ÎN în acest exemplu aceasta este o bară oblică înainte.

6. Modificatori

Comportamentul expresiilor regulate poate fi modificat folosind modificatori. De exemplu, după cum poate ați observat deja, potrivirea unui șir cu o expresie regulată este verificată ținând cont de majuscule și minuscule. Puteți modifica acest comportament folosind modificatorul # (.*?)#g;
# fii atent când folosești /g în context scalar
# detalii aici: http://koorchik.blogspot.com/2011/07/perl-5.html
tipăriți „$_ \n" pentru (@cuvinte);

După cum sa menționat mai sus, un punct denotă orice caracter cu excepția caracterului newline. Puteți modifica acest comportament folosind un modificator /s:

# extrage conținutul articolului din fișierul HTML,
# care poate conține mai mult de una sau două rânduri
meu ($articol) = $html =~ m #

(.*?)
#s;

Apropo, dacă într-o expresie obișnuită trebuie să indicați „orice caracter” fără a utiliza un modificator /s, folosiți expresia [\d\D]. Înseamnă „orice caracter care este o cifră sau nu este o cifră”, adică orice caracter.

În cele din urmă, nimic nu te împiedică să folosești mai mulți modificatori în același timp:

# scoateți totul cu caractere aldine din fișierul HTML
@cuvintele mele = $html =~ m # (.*?)#gi;
# va funcționa pentru , sau chiar

Plus: Un alt modificator util este /o. Înseamnă „compilați expresia regulată o singură dată”. ÎN uneleÎn cazuri, acest modificator poate accelera semnificativ scriptul. Cu toate acestea, nu sunt sigur că este acceptat oriunde, în afară de Perl. Mulțumesc pentru bacșiș tovarăș

Expresiile regulate sunt o modalitate utilizată pe scară largă de a descrie modele pentru căutarea textului și verificarea dacă textul se potrivește cu un model. Metacaracterele speciale vă permit să specificați, de exemplu, că căutați un subșir la începutul șirului de intrare sau un anumit număr de repetări ale subșirului.

La prima vedere, expresiile regulate par înfricoșătoare (bine, la a doua vedere par și mai înfricoșătoare ;)).

Vă recomand cu tărie să vă „jucați” cu programul demo pentru Windows REStudio furnizat în kitul de distribuție - acest lucru vă va permite să înțelegeți mai bine principiul expresiilor regulate și să vă depanați propriile expresii. TestRExp include, de asemenea, multe exemple de expresii.

Mai jos este o descriere a unui subset de sintaxă a expresiilor regulate care funcționează în aproape toate implementările și este susținută de biblioteca mea Delphi, care este inclusă ca standard în Lazarus (Free Pascal).

Să începem introducerea în expresiile regulate!

Comparație simplă

Orice caracter se potrivește cu el însuși, cu excepția cazului în care aparține metacaracterelor speciale descrise mai jos.

Secvența de caractere se potrivește cu aceeași secvență din șirul de intrare, astfel încât modelul bluh se va potrivi cu subșirul bluh din șirul de intrare. Până acum totul este simplu, nu-i așa?

Dacă metacaracterele sau secvențele de evadare trebuie tratate ca caractere obișnuite, acestea trebuie să fie precedate de un caracter \, de exemplu, metacaracterul ^ se potrivește de obicei cu începutul rândurilor, totuși, dacă este scris ca \^ se va potrivi cu caracterul ^, \ \ se potrivește cu \ etc.

Exemple:

foobar găsește „foobar” \^FooBarPtr găsește „^FooBarPtr”

Secvențe de evadare

Orice caracter poate fi specificat folosind o secvență de escape, așa cum se face în C sau Perl: \n înseamnă începutul unei linii, \t înseamnă o tabulație etc. În general, \xnn , unde nn este o secvență hexazecimală cifre, înseamnă caracter cu cod ASCII nn. Dacă trebuie să specificați un caracter pe doi octeți (Unicode), utilizați formatul \x(nnnn) , unde nnnn este una sau mai multe cifre hexazecimale.

\xnn caracter s cod hexazecimal nn \x(nnnn) caracter cu cod hexazecimal nnnn (mai mult de un octet pot fi specificati numai în modul (tregexpr_interface.html#unicode))| \t tab (HT/TAB), puteți, de asemenea, \x09 \n linie nouă (NL), puteți, de asemenea, \x0a \r retur car (CR), puteți, de asemenea, \x0d| Traducere în format \f (FF), puteți și \x0c| \a apel (BEL), puteți, de asemenea, \x07| \e escape (ESC), de asemenea \x1b|

Exemple:

foo\x20bar găsește „foo bar” (notați spațiul din mijloc) \tfoobar găsește „foobar” precedat de o filă

Liste de simboluri

Puteți defini o listă prin includerea caracterelor în . Lista se va potrivi cu orice caracter listat în ea.

Dacă primul caracter al listei (imediat după [) este ^, atunci o astfel de listă se potrivește cu orice caracter care nu este listat în listă.

Exemple:

foobr găsește „foobar”, „foober”, etc. dar nu „foobbr”, „foobcr” etc.

foob[^aeiou]r găsește „foobbr”, „foobcr”, etc.. dar nu „foobar”, „foober”, etc.

În cadrul unei liste, caracterul - poate fi folosit pentru a defini intervale de caractere, de exemplu a-z reprezintă toate caracterele între a și z, inclusiv.

Exemple:

Dacă trebuie să includeți caracterul însuși în listă, plasați-l la începutul sau la sfârșitul listei sau precedați-l cu \. Dacă trebuie să includeți caracterul ] în listă, plasați-l chiar la început sau precedați-l cu \. [-az] „a”, „z” și „-” „a”, „z” și „-” „a”, „z” și „-” toate cele 26 mici litere latine

de la „a” la „z” [\n-\x0D] #10, #11, #12, #13.

[\d-t] cifră, „-” sau „t”. -a] caracter din intervalul „]”..”a”. Metacaracterele

Metacaracterele sunt

caractere speciale

Exemple:

, care sunt cel mai important concept în expresiile regulate. Există mai multe grupuri de metacaractere.

Metacaracterul ^ se potrivește implicit numai la începutul textului introdus, iar metacaracterul $ se potrivește doar la sfârșitul textului. Separatoarele de linii interne prezente în text nu se vor potrivi cu ^ și $ .

Cu toate acestea, dacă trebuie să tratați textul ca pe mai multe linii, astfel încât ^ să se potrivească după fiecare separator de linie din text și $ să se potrivească înainte de fiecare delimitator, atunci puteți include modificatorul /m.

Metacaracterele \A și \Z sunt similare cu ^ și $, dar nu sunt afectate de modificatorul /m, adică. se potrivesc întotdeauna doar la începutul și la sfârșitul întregului text introdus.

Metacaracter. implicit se potrivește cu orice caracter, totuși, dacă dezactivați modificatorul /s, atunci. nu se potrivesc cu separatorii de linii.

TRegExpr interpretează separatorii de linii așa cum se recomandă pe www.unicode.org:

^ se potrivește cu începutul textului introdus și, de asemenea, dacă este inclus modificatorul /m, punctul imediat după \x0D\x0A , \x0A sau \x0D (dacă utilizați versiunea Unicode

$ se potrivește cu sfârșitul textului introdus și, de asemenea, dacă este inclus modificatorul /m, punctul care precede imediat \x0D\x0A , \x0A sau \x0D (dacă utilizați versiunea Unicode a TRegExpr, atunci și \x2028 sau \ x2029 sau \x0B sau \x0C sau \x85). Rețineți că nu se potrivește în secvența \x0D\x0A .

Se potrivește cu orice caracter, dar dacă modificatorul r /s este dezactivat, atunci. nu se potrivește cu \x0D\x0A și \x0A și \x0D (dacă utilizați versiunea Unicode a TRegExpr, nu se potrivește cu \x2028 și \x2029 și \x0B și \x0C și \x85).

Vă rugăm să rețineți că ^.*$ (model pentru linie goală) nu se potrivește cu șirul gol de forma \x0D\x0A , dar se potrivește cu \x0A\x0D .

Puteți reconfigura comportamentul de mai sus atunci când procesați texte cu mai multe linii - vedeți descrierile proprietăților LineSeparators și LinePairedSeparator, de exemplu, puteți reconfigura pentru a utiliza numai separatoare de linie Unix \n sau numai separatoare DOS/Windows \r\n sau separatoare mixte (ambele configurate implicit) sau chiar definiți-vă propriile separatoare de linii!

Metacaractere - liste standard de personaje

\w caracter alfanumeric sau „_” \W nu \w \d caracter numeric \D nu \d \s orice caracter „spațiu alb” (implicit este [ \t\n\r\f]) \S nu \ s

Listele standard \w , \d și \s pot fi utilizate și în interiorul listelor de caractere.

Exemple:

foob\dr găsește „foob1r”, „”foob6r”, etc. dar nu „foobar”, „foobbr”, etc. foob[\w\s]r găsește „foobar”, „foob r”, „foobbr” etc. dar nu „foob1r”, „foob=r” etc.

Metacaracterele - opțiuni

Puteți defini o listă de opțiuni folosind metacaracterul | pentru a le separa, de exemplu fee|fie|foe se va potrivi cu fee sau fie sau foe , (la fel ca f(e|i|o)e). Ca primă opțiune, totul de la metacaracterul anterior (sau [ sau de la începutul expresiei la primul metacaracter | este perceput, ca ultimă opțiune - totul de la ultimul | până la sfârșitul expresiei sau la cel mai apropiat metacaracter) . De obicei, pentru a evita confuzia, setul de opțiuni este întotdeauna cuprins între paranteze, chiar dacă acest lucru ar putea fi făcut fără.

Opțiunile sunt încercate începând de la prima și încercările sunt finalizate de îndată ce este posibil să se selecteze una în care se potrivește întreaga parte ulterioară a expresiei (pentru mai multe detalii, vezi mecanismul de funcționare). Aceasta înseamnă că variantele nu vor furniza neapărat un comportament lacom. De exemplu, dacă aplicați expresia foot|foot la șirul de intrare barefoot , acesta va găsi foo deoarece este prima opțiune care permite ca întreaga expresie să se potrivească.

Rețineți că metacaracterul | este perceput ca un caracter obișnuit în interiorul listelor de caractere, de exemplu, înseamnă exact la fel ca .

Exemple:

foo(bar|foo) se potrivește cu „foobar” sau „foofoo”.

Metacaracterele - subexpresii

Metacaracterele (...) pot fi folosite și pentru a specifica subexpresii

  • După finalizarea căutării unei expresii, puteți accesa orice subexpresie folosind proprietățile MatchPos, MatchLen și Match și, de asemenea, puteți înlocui subexpresii într-un șablon folosind metoda Substitute).

Subexpresiile sunt numerotate de la stânga la dreapta, în ordinea în care apar parantezele de deschidere.

Prima subexpresie are numărul 1 (întreaga expresie este 0", poate fi accesată în Substitute fie $0" fie $&).

Exemple:

(foobar)(8,10) găsește un șir care conține 8, 9 sau 10 copii ale „foobar” foob(|a+)r găsește „foob0r”, „foob1r”, „foobar”, „foobaar”, „foobaar”, etc. .

Metacaracterele - backlink-uri

Metacaracterele de la \1 la \9 sunt tratate ca backlink. \ .

Exemple:

se potrivește cu subexpresia găsită anterior #

(.)\1+ găsește „aaaa” și „cc”. (.+)\1+ se potrivește, de asemenea, cu „abab” și „123123” ([””]?)(\d+)\1 se potrivește cu „13” (între ghilimele duble) sau „4” (între ghilimele simple) sau 77 (fără ghilimele), etc.

Modificatori

Modificatorii sunt utilizați pentru a schimba modurile de funcționare ale TRegExpr.

Puteți schimba modificatorii în mai multe moduri.

De asemenea, puteți atribui o valoare proprietății corespunzătoare a instanței obiectului TRegExpr (de exemplu, ModifierX pentru a modifica modificatorul /x sau ModifierStr pentru a modifica mai mulți modificatori simultan). Valorile implicite pentru noile instanțe ale obiectelor TRegExpr sunt definite în , de exemplu RegExprModifierX definește valoarea implicită pentru ModifierX.

i

Modul care nu ține seama de majuscule și minuscule (în mod implicit folosește limba implicită selectată în sistemul de operare), (vezi și InvertCase)

m

s

g

Nu este un modificator standard. Opriți-l, comutați toate repetoarele în modul „non-lacom” (acest modificator este activat implicit). Aceste. Dacă îl dezactivați, atunci toate + funcționează ca +? , * Cum *? etc.

x

Vă permite să formatați șablonul pentru a fi mai ușor de citit (vezi descrierea de mai jos).

r

Nu este un modificator standard. Dacă este activat, atunci intervale tip a-z include, de asemenea, litera ё, A-Z include Ё și a-Z include toate literele rusești în general.

Modificatorul /x face ca TRegExpr să ignore spațiile, tabulatorii și separatorii de linii, permițând formatarea textului expresiei. În plus, dacă este întâlnit caracterul #, atunci toate caracterele ulterioare până la sfârșitul rândului sunt tratate ca un comentariu, de exemplu:

((abc) # Comentariu 1 | # Spațiile din expresie sunt de asemenea ignorate (efg) # Comentariu 2)

Desigur, aceasta înseamnă că, dacă trebuie să inserați un spațiu, tabulație sau separator de linii sau # într-o expresie, atunci în modul extins (/x) acest lucru se poate face numai precedându-le cu / sau folosind /xnn (în listele de caractere , toate aceste personaje sunt tratate ca de obicei)

Extensii Perl

(?imsxr-imsxr)

Vă permite să modificați valorile modificatorului

Exemple:

(?i)Sankt-Petersburg găsește „Sankt-Petersburg” și „Sankt-Petersburg” (?i)Saint-(?-i)Petersburg găsește „Sankt-Petersburg” dar nu „Sankt-Petersburg” (?i)(Sfântul -)?Petersburg găsește „Sankt-Petersburg” și „sfântul-petersburg” ((?i)Saint-)?Petersburg găsește „sfântul-Petersburg”, dar nu „sfântul-petersburg”

Ce sunt expresiile regulate?

Dacă ați lucrat vreodată cu linia de comandă, probabil că ați folosit măști de nume de fișiere. De exemplu, pentru a șterge toate fișierele din directorul curent care încep cu litera „d”, ați putea scrie

Expresiile regulate sunt un instrument similar, dar mult mai puternic pentru a găsi șiruri, a le testa pentru a vedea dacă se potrivesc cu un model și multe altele. lucrare similară. Denumirea în engleză a acestui instrument este Expresii regulate sau doar RegExp. Strict vorbind, expresiile regulate sunt un limbaj special pentru descrierea modelelor de șiruri.

Implementarea acestui instrument variază în funcție de limbi diferite programare, deși nu prea mult. În acest articol ne vom concentra în primul rând pe implementarea expresiilor regulate compatibile cu Perl.

Bazele sintaxei

În primul rând, este de remarcat faptul că orice șir în sine este o expresie regulată. Deci, expresia

Haha, evident, se va potrivi cu șirul „Haha” și doar atât. Expresiile regulate sunt sensibile la majuscule, așa că șirul „haha” (minuscule) nu se va mai potrivi cu expresia de mai sus.

Cu toate acestea, ar trebui să fiți atenți aici - ca orice limbă, expresiile regulate au caractere speciale care trebuie să fie eliminate. Iată lista lor:

. ^ $ * + ? ( ) \ | (). Se efectuează ecranarea în mod obișnuit- adăugarea \ înainte de caracterul special.

Set de caractere

Să presupunem că vrem să găsim toate interjecțiile dintr-un text care indică râsul. Doar

Haha nu ne va potrivi - la urma urmei, „Hehe”, „Hoho” și „Hihi” nu vor cădea sub ea. Și problema cu cazul primei litere trebuie rezolvată cumva.

Aici seturile ne vor veni în ajutor - în loc să specificăm un anumit caracter, putem scrie o listă întreagă, iar dacă vreunul dintre caracterele enumerate apare în locul specificat în șirul care este examinat, șirul va fi considerat adecvat. Seturile sunt scrise între paranteze drepte - model

Se va potrivi cu oricare dintre caracterele „a”, „b”, „c” sau „d”.

Setul interior b O Majoritatea caracterelor speciale nu au nevoie de evadare, ci de utilizare

\ în fața lor nu va fi considerată o eroare. Este încă necesar să scapi de caracterele „\” și „^”, și, de preferință, „]” (deci, înseamnă oricare dintre caracterele „]” sau „[”, în timp ce [x] este exclusiv secvența „[ x]"). Comportamentul aparent neobișnuit al expresiilor regulate cu caracterul „]” este de fapt determinat de reguli binecunoscute, dar este mult mai ușor să scapi pur și simplu de acest personaj decât să le reții. În plus, simbolul „-” trebuie să fie eliminat pentru a seta intervale (vezi mai jos).

Dacă imediat după

[notati simbolul ^ , apoi setul va capata sensul opus - orice simbol altul decat cele indicate va fi considerat potrivit. Astfel, modelul [^xyz] se potrivește cu orice caracter, cu excepția „x”, „y” sau „z”.

Deci, folosind acest instrument la cazul nostru, dacă scriem

[Xx][aoie]x[aoie] , apoi fiecare dintre liniile „Haha”, „hehe”, „hihi” și chiar „Hoho” se va potrivi cu modelul.

Clase de caractere predefinite

Pentru unele seturi care sunt folosite destul de des, există șabloane speciale. Deci, pentru a descrie oricare caracter de spațiu alb(spațiu, tab, ruptură de linie).

\s , pentru numere - \d , pentru caractere latine, numere și litere de subliniere „_” - \w .

Dacă este necesar să descrii vreun caracter, se folosește un punct pentru aceasta -

Dacă clasele specificate sunt scrise cu majuscule (\S, \D, \W), atunci ele își vor schimba sensul în sensul opus - orice caracter care nu este un spațiu alb, orice caracter care nu este un număr și orice alt caracter decât Alfabetul latin, cifrele sau, respectiv, liniuța de subliniere.

De asemenea, folosind expresii regulate, este posibil să se verifice poziția unei linii în raport cu restul textului. Expresie

\b denotă o limită de cuvânt, \B este o limită non-cuvânt, ^ este începutul textului și $ este sfârșitul. Deci, conform modelului \bJava\b, primele 4 caractere vor fi găsite în linia „Java și JavaScript”, iar conform modelului \bJava\B, vor fi găsite de la 10 la 13 caractere (ca parte a cuvântul „JavaScript”).

Intervalele

Poate fi necesar să desemnați un set care conține litere, de exemplu, de la „b” la „f”. În loc să scrii

[bvgdezziklmnoprstuf] puteți folosi mecanismul de gamă și puteți scrie [b-f] . Astfel, modelul x corespunde șirului „xA6”, dar nu corespunde „xb9” (în primul rând, datorită faptului că numai majuscule, în al doilea rând, datorită faptului că 9 nu este inclus în intervalul 0-8).

Mecanismul gamelor este deosebit de relevant pentru limba rusă, deoarece pentru aceasta nu există nicio construcție similară

\w . Pentru a desemna toate literele alfabetului rus, puteți folosi modelul [а-яА-ЯеО] . Vă rugăm să rețineți că litera „е” nu este inclusă în gama generală de litere și trebuie specificată separat.

Cuantificatori (indicând numărul de repetări)

Să revenim la exemplul nostru. Dacă interjecția „râzând” ar avea mai mult de o vocală între x, cum ar fi „Haahaaaa”? Vechiul nostru sezon regulat nu ne va mai putea ajuta. Aici va trebui să folosim cuantificatori.

Rețineți că cuantificatorul se aplică doar caracterului care vine înaintea lui.

Unele construcții utilizate frecvent au primit notații speciale în limbajul expresiei regulate:

Deci, cu ajutorul cuantificatorilor ne putem îmbunătăți modelul pentru interjecții

[Xx][aoeee]+x[aoeee]* , și va putea recunoaște liniile „Haaha”, „heeeeeh” și „Hihii”.

Cuantificare leneșă

Să presupunem că ne confruntăm cu sarcina de a găsi toate etichetele HTML dintr-un șir

Tproger- al meu Dragă site despre programare!

Soluția evidentă

<.*>nu va funcționa aici - va găsi întregul șir, pentru că începe cu și se termină cu o etichetă de paragraf. Adică, conținutul etichetei va fi considerat a fi șirul p> Tproger- al meu Dragă site despre programare!

Acest lucru se întâmplă din cauza faptului că în mod implicit cuantificatorul funcționează conform așa-numitului. lacom algoritm - încearcă să returneze cel mai lung șir posibil care îndeplinește condiția. Există două moduri de a rezolva problema. Primul este să folosiți expresia

<[^>]*> , ceea ce va împiedica ca paranteza unghiulară să fie considerată conținutul etichetei. Al doilea este de a declara cuantificatorul nu lacom, dar leneş. Se face acest lucru prin adăugarea în dreapta cuantificatorului de caractere?<.*?> .

. Aceste. pentru a căuta toate etichetele, expresia se va întoarce la

Cuantificare geloasă Uneori, pentru a crește viteza de căutare (mai ales în cazurile în care șirul nu se potrivește cu expresia regulată), puteți împiedica algoritmul să revină la pașii anteriori caută pentru a găsi potriviri posibile pentru restul expresiei regulate. Se numește gelos

cuantificare. Cuantificatorul este făcut gelos prin adăugarea simbolului în dreapta acestuia

O altă utilizare a cuantificării geloase este excluderea potrivirilor nedorite. Astfel, modelul ab*+a din șirul „ababa” se va potrivi doar cu primele trei caractere, dar nu și cu caracterele de la al treilea la al cincilea, deoarece caracterul „a”, care se află în poziţia a treia, a fost deja folosit pentru primul rezultat.

Grupuri de paranteze

Pentru șablonul nostru de interjecție „râzând”, singurul lucru mic rămas este să luăm în considerare faptul că litera „x” poate apărea de mai multe ori, de exemplu, „Hahahahaaahaahoooo”, și se poate termina chiar cu litera „x”. Probabil că trebuie să utilizați un cuantificator de grup aici

[aioe]+x, dar dacă scriem doar [aioe]x+ , atunci cuantificatorul + se va aplica doar caracterului „x” și nu întregii expresii. Pentru a remedia acest lucru, expresia trebuie pusă între paranteze: ([aioe]x)+ .

Astfel expresia noastră devine

[Xx]([aioe]x?)+ - mai întâi există un „x” majuscul sau minuscul, apoi un număr arbitrar de vocale diferit de zero, care (posibil, dar nu neapărat) sunt intercalate cu „x” minuscul unic. . Cu toate acestea, această expresie rezolvă problema doar parțial - această expresie va include și linii precum, de exemplu, „hihaheh” - cineva poate râde așa, dar presupunerea este foarte dubioasă. Evident, putem folosi o singură dată un set de toate vocalele și atunci trebuie să ne bazăm cumva pe rezultatul primei căutări. Dar cum?...

Se pare că rezultatul căutării unui grup de paranteze este scris într-o celulă de memorie separată, care poate fi accesată pentru a fi utilizată în părțile ulterioare ale expresiei regulate. Revenind la sarcina de a găsi etichete HTML pe o pagină, este posibil să fie nevoie nu numai să găsim etichetele, ci și să aflăm numele acestora. O expresie regulată ne poate ajuta în acest sens

<(.*?)> .

Tproger- al meu Dragă site despre programare!

Rezultatul căutării pentru toate expresiile regulate: "

», « », «», « », «», «

».
Rezultatul căutării pentru primul grup: „p”, „b”, „/b”, „i”, „/i”, „/i”, „/p”.

Rezultatul unei căutări de grup poate fi referit folosind expresia

\n , unde n este un număr de la 1 la 9. De exemplu, expresia (\w)(\w)\1\2 se potrivește cu șirurile „aaaa”, „abab”, dar nu se potrivește cu „aabb”.

Dacă o expresie este plasată între paranteze doar pentru a-i aplica un cuantificator (nu există planuri de a reține rezultatul căutării pentru acest grup), atunci prima paranteză trebuie adăugată imediat

?: , de exemplu (?:+\w) .

Folosind acest mecanism, ne putem rescrie expresia în formă

[Xx]([aoie])x?(?:\1x?)* .

Transfer

Pentru a verifica dacă un șir satisface cel puțin unul dintre modele, puteți utiliza un analog al operatorului boolean OR, care este scris folosind simbolul

| . Astfel, modelul Anna|Singurătate include liniile „Anna” și, respectiv, „Singurătate”. Este deosebit de convenabil să folosiți enumerarea în cadrul grupurilor de paranteze. Deci, de exemplu (?:a|b|c|d) este complet echivalent (înîn acest caz,

a doua opțiune este de preferat datorită performanței și lizibilității).

Folosind acest operator, putem adăuga expresiei noastre regulate pentru găsirea interjecțiilor capacitatea de a recunoaște râsete precum „Ahahaah” - singurul zâmbet care începe cu o vocală:

[Xx]([aoie])x?(?:\1x?)*|[Aa]x?(?:ah?)+

Servicii utile

Vă puteți exersa și/sau testa expresia obișnuită pe un text fără a scrie cod folosind servicii precum RegExr, Regexpal sau Regex101. Acesta din urmă, în plus, oferă scurte explicații despre modul în care funcționează sistemul obișnuit.

Vă puteți da seama cum funcționează o expresie obișnuită care vă vine în mâini folosind serviciul Regexper - poate construi diagrame ușor de înțeles folosind o expresie regulată. RegExp Builder - designer vizual Funcții JavaScript

0

0

pentru lucrul cu expresii regulate.

Ați lucrat vreodată cu corzi? Da, da, cu acele „matrice de caractere” pe care le cunoaștem și le iubim cu toții. Dacă ați programat în altceva decât în ​​C pur, este sigur să presupuneți că ați făcut, și de mai multe ori. Dar dacă ai de-a face cu o mulțime de șiruri? Sau cu șiruri care nu au fost generate de programul tău? De exemplu, ai citit e-mail, analizați argumentele linie de comandă sau citirea instrucțiunilor scrise de o persoană și au nevoie de o metodă mai structurată de lucru cu toate acestea.

Desigur, puteți repeta fiecare cuvânt sau caracter din toate rândurile. Și un astfel de cod va fi probabil destul de ușor de înțeles. Dar, în aplicațiile la scară largă, acest lucru poate fi inutil de greoi și poate consuma prea mult resurse.

Introducere. Expresia regulată

Fără a intra în jungla informaticii profunde, să definim o expresie regulată.

  • Expresiile regulate sunt reguli care descriu un limbaj formal.
  • Expresiile regulate sunt un tip de limbaj formal care poate fi procesat de o mașină de stări.

Există multe alte definiții ale expresiilor regulate, așa că dacă cele de mai sus nu sunt suficiente pentru a vă face fericit, puteți petrece câteva minute Google.

Introducere. Regex

Acum s-ar putea să ajungem la punctul în care este timpul să ne sperii (dacă nu ați făcut-o deja). Ne vom uita la diferențele dintre ceea ce este inclus în conceptul de expresii regulate în limbajele de programare și ceea ce este inclus în informatica fundamentală.

  • Expresiile regulate din punctul de vedere al informaticii sunt reguli care explică un limbaj formal.
  • Expresiile regulate din punct de vedere al limbajelor de programare sunt o gramatică care exprimă, într-o măsură mai mare, unele limbaj sensibil la context.

Limbile sensibile la context sunt semnificativ mai complexe și mai puternice, așa că de acum încolo vom fi de acord să numim expresiile regulate din limbajul de programare termeni „regex” pentru a sublinia izolarea lor de limbajele formale în general.

Învățați să scrieți regex

Expresiile regulate sunt descrise folosind două bare oblice ( // ) și potrivesc șiruri care se potrivesc cu modelul dintre ele. De exemplu, /Hi/ se potrivește cu „Bună”, astfel încât să putem verifica dacă un șir se potrivește cu acest model.

Caracterele din expresiile regulate sunt potrivite în ordinea în care sunt introduse. Aşa /Salut Lume/ răspunde la șirul „Bună lume”.

Puteți ușura căutarea cuvintelor arbitrare adăugând puțină magie regex: \w se potrivește cu orice „cuvânt” format numai din litere. Numerele sunt identificate folosind același principiu: \d .

Exemplul 1

Grozav, acum putem compara șiruri sau putem verifica dacă se potrivesc cu un anumit model. Ce urmează? Pot expresiile regulate să îndeplinească alte funcții?

Fiți siguri! Să presupunem că am scris un bot de chat IRC care reacționează dacă cineva scrie „Josh”. Botul nostru scanează fiecare mesaj până când găsește o potrivire. Apoi botul răspunde: „Woah, sper că nu vorbești de rău despre prietenul meu Josh!” („O, sper că nu vei vorbi de rău despre prietenul meu Josh!”). Pentru că numai roboții sunt prieteni cu Joshe.

Botul nostru folosește un șablon pentru a compara șirurile /Josh/ . La un moment dat, cineva pe nume Eli va spune: „Eli: Josh, chiar ai nevoie de atâta cofeină?” Botul nostru își va ciuli urechile, va detecta un potrivire și va da răspunsul său neașteptat, ceea ce îl va speria destul de pe Eli. Misiune îndeplinită! Sau nu?

Dacă botul nostru ar fi mai inteligent? Dacă el, de exemplu, s-ar adresa vorbitorului pe nume? Ceva de genul „Woah, sper că nu vorbești rău pe prietenul meu Josh, Eli”.

Cuantificatori (caractere care se repetă)

0 sau mai mult

Putem face asta... Dar mai întâi trebuie să înțelegem câteva lucruri. În primul rând - cuantificatori(pentru repetarea caracterelor). Poate fi folosit * pentru a indica 0 sau mai multe caractere după. De exemplu, /o*/ se poate potrivi atât cu „aaaaaaa” cât și cu „”. Da, ați auzit bine: va răspunde la un șir gol.

* servește la desemnarea a ceva opțional, întrucât simbolul căruia îi corespunde nu trebuie să existe deloc. Dar poate. Și de mai multe ori (teoretic, de nenumărate ori).
Puteți desemna „Josh” cu /Josh/ , dar putem specifica și modelul „Jjjjjjjjjosh” sau „osh”. /J*osh/ .

1 sau mai multe

Pentru a desemna unul sau mai multe caractere este folosit + . Funcționează eficient pe același principiu ca și * , cu excepția faptului că existența a cel puțin unui caracter nu mai este opțională: cel puțin unul trebuie să fie prezent.

Deci putem seta șablonul /J+osh/ șirurile „Josh” sau „Jjjjjjjjjosh”, dar nu „osh”.

de la „a” la „z” [\n-\x0D] #10, #11, #12, #13.

Grozav, ne-am eliberat deja mâinile în multe feluri. Cineva s-ar putea să strige „Joooooosh” chiar acum dacă este deja suficient de supărat...

Dar dacă se enervează atât de mult încât chiar își lovește fața în tastatură de câteva ori? Cum desemnăm „aaavyopshadlorvpt” fără să știm dinainte cât de precis este nasul lui?
Prin utilizarea metacaracterele!

Metacaracterele vă permit să specificați absolut ORICE. Sintaxa lor este . . (Da, punct. Doar un punct.). Punem pariu că îl folosiți foarte mult, așa că nu ezitați să îl folosiți pentru a marca sfârșitul unei propoziții.

Puteți seta „Joooafhuaisggsh” cu expresia /Jo+.*sh/ , combinând cunoștințele dobândite anterior despre caractere repetate și metacaractere. Pentru a fi precis, această expresie se potrivește cu un „J”, unul sau mai multe „o”, zero sau mai multe metacaractere și un „s” și un „h”. Aceste cinci blocuri ne conduc la ceea ce numim...

...grupuri de personaje

Grupuri de caractere- acestea sunt blocuri de caractere în care succesiunea componentelor este importantă. Ele sunt considerate ca un întreg. Folosind * sau + , de fapt specificați o secvență a unui grup de caractere care se repetă, nu doar ultimul caracter.

Acest lucru este util de înțeles ca tehnică de sine stătătoare, dar câștigă o funcționalitate mai mare atunci când este combinat cu simboluri repetate. Grupurile de caractere sunt specificate folosind paranteze (da, acei tipi).
Să presupunem că vrem să repetăm ​​„Jos”, dar nu „h”. ceva de genul „JosJosJosJosJosh”. Acest lucru se poate face cu expresia /(Jos)+h/ . Simplu, nu-i așa?

Dar în sfârșit... Revenind la primul nostru exemplu, cum obținem numele lui Eli în chat-ul nostru IRC din mesajul pe care l-a trimis?

Grupurile de caractere pot servi și pentru a reține subșiruri. Pentru a face acest lucru, ei fac de obicei ceva de genul \1 pentru a determina primul grup specificat.

De exemplu, /(.+) \1/ caz special. Aici vedem un set simboluri aleatorii, repetat o dată sau de mai multe ori, un spațiu după el și apoi repetă din nou exact același set. Deci, o astfel de expresie se va potrivi cu „abc abc”, dar nu cu „abc def”, chiar dacă „def” în sine se potrivește (.*) .

Memorarea potrivirilor este foarte puternică și probabil se reduce la cea mai utilă caracteristică a expresiilor regulate.

Exemplul 2

Pf... În sfârșit, putem reveni la exemplul cu botul de chat IRC. Să ne punem în practică cunoștințele.

Dacă vrem să surprindem numele expeditorului mesajului când scrie „Josh”, expresia noastră ar arăta cam așa: /(\w+): .*Josh.*/ , și putem stoca rezultatul într-o variabilă în limbajul nostru de programare pentru răspuns.

Să ne uităm la expresia noastră regulată. Iată una sau mai multe litere care urmează „ : ”, 0 sau mai multe caractere, „Josh” și din nou 0 sau mai multe caractere.

Notă: /.*cuvânt.*/ este o modalitate simplă de a specifica un șir care conține „cuvânt”, care poate conține sau nu alte caractere.

În Python ar arăta astfel:
import re
model = re.compile(ur"(\w+): .*Josh.*") # Regex-ul nostru
șir = u"Eli: Josh du-te să-ți muți rufele" # Șirul nostru
potriviri = re.match(pattern, string) # Testează șirul
who = matchs.group(1) # Obțineți cine a spus mesajul
print(cine) # "Eli"
Observați că am folosit .grup(1) la fel ca \1 . Acest lucru nu este nimic nou, cu excepția utilizării expresiilor regulate în Python.

Începutul și sfârșitul

Până în acest punct, am presupus că subșirurile pe care le căutăm pot fi localizate oriunde în șir. De exemplu, /(Jos)+h/ se potrivește cu orice șir care conține „Jos-repeated-h” în orice locație.

Dacă vrem ca șirul să înceapă cu acest model? Acesta poate fi notat ca /^(Jos)+h/ , Unde ^ se potrivește cu începutul liniei. De asemenea, $ marchează sfârșitul liniei.

Acum, dacă vrem să specificăm un șir care conține doar „Jos-repeating-h”, vom scrie /^(Jos)+h$/ .

Enumerarea expresiilor

Imaginați-vă că scrieți o expresie obișnuită pentru o rețetă de sandvișuri. Nu știi dacă clientul preferă pâinea albă sau neagră, dar tot trebuie să alegi doar una. Cum se adaugă selecție la regex? Prin utilizarea transferuri!

Acestea vă permit să specificați seturi de valori posibile pentru un grup de caractere. Arata cam asa: (alb|grâu) . În contextul exemplului nostru de sandviș, una dintre opțiuni va fi acceptată - fie „alb”, fie „grâu”.

Pentru a desemna enumerări, [parantezele pătrate] sunt folosite oarecum diferit. În loc de întreg șirul, fiecare caracter este o variantă aici. Acest lucru poate fi util pentru expresiile regulate complexe, deoarece puteți înlocui un singur caracter cu un set mai complex.

(.)\1+ găsește „aaaa” și „cc”. (.+)\1+ se potrivește, de asemenea, cu „abab” și „123123” ([””]?)(\d+)\1 se potrivește cu „13” (între ghilimele duble) sau „4” (între ghilimele simple) sau 77 (fără ghilimele), etc.

Vorbeam despre regex cu /două bare oblice/, nu? Știm ce este între ei, dar ce ar trebui să fie afară?

Întorsătură neașteptată: nimic!

…stânga. Partea dreaptă, dimpotrivă, poate conține multe, multe lucruri utile. Este păcat că nu am spus un cuvânt despre asta atât de mult timp!
Modificatori definiți regulile prin care se aplică expresiile regulate.

Iată o listă a modificatorilor principali (de la Regex101.com):

Modificator Nume Descriere
g globală Toate meciurile
m multi-linie ^ și $ se potrivesc cu începutul și sfârșitul fiecărei linii
i insensibil Comparație fără majuscule
x prelungit Spațiile și textul după # sunt ignorate
X suplimentar \ cu o literă arbitrară care nu are o semnificație specială returnează o eroare
s o singură linie Ignoră liniile noi
u unicode Șirurile de șabloane sunt procesate ca UTF-16
U nelacom În mod implicit, regex utilizează cuantificare leneșă. Modificatorul U face cuantificarea lacomă
O ancorat Modelul este forțat să ^
J duplicat Permite nume de submodel duplicat

Pentru claritate, toate exemplele anterioare au fost sensibile la majuscule și minuscule. Aceasta înseamnă că dacă înlocuiți chiar și o literă mică cu una mare sau invers, șirul nu se va mai potrivi cu modelul. Dar îl puteți face indiferent de majuscule și minuscule folosind un modificator i .

Să presupunem că Eli s-a supărat atât de mult încât a început să bombardeze chat-ul cu mesaje cu SCRISORI DE DIFERITE REGISTRE. Acest lucru nu ne sperie, pentru că sunt deja aici! Putem defini cu ușurință expresia furioasă „Urăsc să trăiesc cu JOSH!!!” /i ha+te living with josh!+/i . Regex-ul nostru este acum mai ușor de citit și, de asemenea, mult mai puternic și util. Uimitor!

Puteți juca singur cu diferiți modificatori. În opinia mea, în general vei beneficia cel mai mult igm .

Ce urmează?

Sper că acest articol mi-a permis să privesc diferit lucrul cu șiruri și să-l fac mai rezonabil. Am zgâriat doar suprafața, dar ați învățat deja cum să utilizați regex pentru a rezolva unele probleme.

Există multe caractere și combinațiile lor folosite în expresiile regulate. De obicei, le veți întâlni în timp ce explorați Stack Overflow, dar puteți ghici semnificația unora din exemplele anterioare (de exemplu, \n - simbol de tranziție la linie nouă). S-a pus bazele, dar mai sunt multe de învățat.

Găsi lista completa combinații de simboluri și, de asemenea, vă puteți testa cunoștințele.
Dacă ți s-a părut o bucată de tort, încearcă cuvintele încrucișate regex. Chiar te vor face să transpiri.

După punct

Acest articol este o traducere a ghidului lui Josh Hawkins. Josh este un dezvoltator web pasionat din Alabama. A început să programeze la vârsta de nouă ani, concentrându-se pe jocuri video, desktop și unele aplicații mobile. Cu toate acestea, în timpul unui stagiu în 2015, Josh a descoperit dezvoltarea web și a pătruns în lumea open source legată de acest domeniu.

Iată câteva exemple de expresii regulate.

    karova - evident un șablon în care se încadrează cuvântul karova;

    \b(shift|unshift|pop|push|splice)\b - oricare dintre cuvintele enumerate;

    ^\s+ - unul sau mai multe spații sau file la începutul unei linii.

În expresiile regulate, caracterele alfanumerice se reprezintă de obicei. De exemplu, modelul Hello specifică căutarea caracterului H urmat de e , apoi l , etc.

Dacă un simbol este dificil sau incomod de specificat literal, puteți folosi literale deja cunoscute de noi: \n , \t și altele. Aceasta înseamnă că caracterul \ inclus în expresia regulată nu se mai poate autodesemna, deoarece schimbă semnificația caracterului care îl urmează: în special, litera n, împreună cu caracterul backslash precedent, denotă caracterul de sfârșit de linie. Dacă doriți să includeți caracterul \ însuși în model, ar trebui să utilizați literalul \\ .

Există și alte simboluri cărora li se dă o semnificație specială în tipare, în loc să se reprezinte. Astfel de simboluri sunt numite metacaracterele. Să dăm câteva exemple de metacaractere, fără a indica încă semnificația lor specială (lista nu este exhaustivă): \.-()()?*+^$| .

Unele personaje nu se dovedesc întotdeauna a fi metacaractere, ci doar atunci când se încadrează într-un anumit context. Unele metacaractere au semnificații diferite în funcție de context.

Dacă trebuie să inserați un metacaracter într-o expresie obișnuită, făcându-l să nu mai aibă sens, ar trebui să îl protejați ( scut), plasând o bară oblică inversă în fața lui. De exemplu, un semn plus într-o expresie regulată este inserat ca \+ .

Modelul desemnează unul dintre caracterele enumerate între paranteze drepte. Dacă, de exemplu, ne interesează cuvântul Bună ziua, nu contează dacă este scris cu majuscule sau mic, modelul va fi astfel: ello. Iată un model care reprezintă o literă vocală mică în alfabetul englez: . Un alt exemplu este o clasă de caractere formată din ambele paranteze drepte: [\[\]] .

Dacă o clasă de caractere include caractere care sunt consecutive în tabelul de coduri, este suficient să indicați primul și ultimul caracter, inserând o cratimă între ele. De exemplu, o clasă care reprezintă orice cifră zecimală poate fi specificată ca . O literă din alfabetul englez este notă cu (aici ne bazăm pe faptul că în orice tabel de coduri majuscule și mici litere engleze intra in blocuri continue in ordine alfabetică; cu toate acestea, un bloc de litere mici nu urmează imediat un bloc de litere mari).

Este posibil să se definească o clasă de caractere formată din toate caracterele, cu excepția celor enumerate - așa-numitele negarea clasei de caractere. Pentru a face acest lucru, un semn circumflex ^ este inserat imediat după paranteza pătrată de deschidere înainte de enumerare. Orice caracter care nu este un număr poate fi reprezentat ca [^0-9] .

Există notații speciale pentru unele clase de caractere populare:

Când un șir este potrivit cu o expresie regulată, fiecare caracter sau clasă de caractere din șablon este potrivit cu un caracter din șir. Dar există construcții care indică nu prezența un anumit simbol, ci un loc specific (gol) în linie. Astfel de construcții se numesc legături, sau ancore

Cele mai frecvent utilizate ancore sunt ancora (^) și capătul ($) al șirului. Ancora de început a liniei ar trebui să fie plasată la începutul modelului, iar ancora de capăt ar trebui să fie plasată la sfârșit.

De exemplu, cuvintele rusești precum antidot, antisemitism sau antiparticulă se potrivesc tiparului ^anti. Fără o legătură, liniile care nu încep cu „anti-”, dar care conțin această combinație de litere în interior, ar fi de asemenea potrivite, de exemplu, mercantilism. Pentru a căuta cuvinte care se termină în „-tsya” aveți nevoie de modelul tsya$ (sîntem aproape 100% siguri că toate astfel de cuvinte sunt verbe reflexive la infinitiv). Nimic nu vă împiedică să utilizați ambele legături în șablon.

O altă ancoră utilă este cuvântul boundary anchor \b . Se potrivește spațiul dintr-un șir dintre caractere, dintre care unul este din clasa \w și celălalt din clasa \W (în orice ordine). Această ancoră se poate potrivi și cu începutul sau sfârșitul unui șir (în acest caz, șirul este considerat a fi înconjurat de caractere imaginare din clasa \W).

Dacă căutăm un fragment care ar trebui să se potrivească cu unul dintre mai multe modele, trebuie să enumerăm aceste modele, împărțind linie verticală| . De exemplu, luni|marți|miercuri|joi|vineri|sâmbătă|duminică. Pentru ca lista de alternative să fie o unitate independentă și separată de cele învecinate, aceasta trebuie inclusă între paranteze. De exemplu, modelul Respected înseamnă șirul Respected urmat de unul dintre șirurile th sau th. Fără paranteze, modelul Dear|aya ar indica unul dintre șirurile Dear sau aya. Parantezele au un efect secundar important, care va fi discutat în secțiunea Grupare și capturare.

Pentru a indica de câte ori se poate repeta un model, așa-numitul cuantificatori(din cuvântul latin cuantic- Câți):

Cuantificatori * , + și ? sunt redundante deoarece pot fi exprimate diferit folosind bretele. Și anume, * este echivalent cu (0,) , + este echivalent cu (1,) , nu? - la fel ca (0,1) . Dar acești cuantificatori sunt foarte des folosiți și, prin urmare, merită denumiri separate.

Dacă modelul căruia i se aplică cuantificatorul este ceva mai complex decât un singur caracter sau o singură clasă de caractere, acesta trebuie să fie cuprins între paranteze.

Iată câteva exemple:

    ^\d+$ - o secvență de una sau mai multe cifre zecimale (un model pentru numere întregi nenegative în notație zecimală);

    ^\-?\d+$ - același lucru, dar pentru toate numerele întregi (eventual negative);

    ^\-?(\d+(\.\d*)?|\.\d+)$ - model pentru numere reale;

Să ne uităm la ultimul exemplu mai detaliat. Pe lângă semnul minus opțional de la început, modelul conține un grup cu două alternative: \d+(\.\d*)? și \.\d+ . Prima alternativă include o parte întreagă necesară \d+ (cel puțin o cifră), urmată de o parte fracțională opțională (\.\d*)? . Partea fracțională, dacă există una, are un punct zecimal și, eventual, mai multe cifre. Astfel, această alternativă corespunde liniilor 15, 15., 15.487. O altă alternativă este necesară pentru șiruri precum .618 cu lipsă întreaga parte- în multe limbaje informatice această postare are dreptul de a exista.

Dacă cele mai simple elemente ale unei expresii regulate - caractere, clase de caractere și ancore - sunt scrise într-un rând, aceasta înseamnă că atunci când un model este căutat într-un șir, aceste elemente vor fi potrivite cu părți ale șirului secvențial, în aceeași secvență . Acest ordin este încălcat dacă se aplică alternative. Vă puteți imagina că o expresie regulată compusă este formată din expresii simple folosind două operații: îmbinare secvențială ( compozitii) și alternative. Compoziția este un analog al operației de înmulțire din aritmetică. O alternativă este un analog al adăugării. Prima asemănare cu aritmetica este că operația alternativă are o prioritate mai mică decât compoziția, așa că poate fi necesară gruparea parantezelor, ca în exemplul Dear.

Nota

Multe, deși nu toate, legi ale aritmeticii se aplică și expresiilor regulate:

comutativitatea alternativei x | y = y | x ; asociativitatea alternativei x | y | z = x |

În această ciudată aritmetică a expresiilor regulate, legea comutativității pentru compoziție nu este valabilă. În plus, nu există un analog de zero datorită relației evidente x |

x = x . Rolul de unitate (dreapta și stânga) pentru compoziție este jucat de un șablon gol (să-l notăm 𝟙): 𝟙 ⁣ x = x ⁣ 𝟙 = x. Cuantificatorii de forma ( n ) joaca rolul de a ridica la a n-a putere.

Pe lângă funcția de grupare, parantezele îndeplinesc o funcție de captare. Principalul rezultat al potrivirii unui șir cu un model este răspunsul la întrebarea: șirul se potrivește cu modelul? Dar, în plus, este adesea necesar să se determine ce fragment sau fragmente dintr-un șir se potrivesc cu anumite fragmente într-o expresie regulată.

Sau poate acid etilendiamină-N N N ′ N ′-tetraacetic?

Să luăm în considerare un exemplu în care în text se găsesc referiri la diferiți acizi. Amintirile noastre de școală din chimie ne-au condus la ideea că numele acizilor se termină fie în vaya, fie naya, fie taya și apoi, după un spațiu, urmează cuvântul acid. Facem un șablon: \S+[int]acid. Potrivim textul cu șablonul. Noroc! Dar, cineva se întreabă, ce fel de acid a fost menționat în text? Sărat? Sulf? Azot? Plavikova? Clor? Cloric? Ipocloros? Lămâie? Cianil? Dezoxiribonucleic? Aici mânerul este la îndemână. Anexăm acea parte a șablonului care, conform planului nostru, ar trebui să corespundă numelui dintre paranteze: (\S+[int]th) acid. Apoi mașina, după ce a găsit o mențiune a acidului în text, își va salva numele (ceea ce corespunde fragmentului de șablon cuprins între paranteze) într-o variabilă specială -

tampon de captare O expresie regulată poate conține mai multe grupuri de captură. Astfel de grupuri nu numai că se pot urma unul pe celălalt, ci și se pot cuibăre unul în celălalt. Cu alte cuvinte, expresia regulată trebuie să fie echilibrată în raport cu parantezele în același sens ca cel discutat în capitolul 23." Verificarea echilibrului dintre paranteze

2 4 5 ┝┑ ┝┑┝┑ (()(()())) │ ┝━━━━┙│ │ 3 │ ┝━━━━━━━━┙ 1

Dacă se dorește, un grup poate fi exclus din numerotare, adică lipsit de funcția sa „agresivă”, lăsând doar funcția de grupare. Pentru a face acest lucru, în loc de delimitatori de grup (⋯), folosim ( ?: ⋯) . Există un semn de întrebare aici Nu denotă un cuantificator deoarece cuantificatorul trebuie să fie precedat fie de un caracter, de o clasă de caractere, fie de un grup.

Utilizarea grupurilor de captură numerotate nu este întotdeauna convenabilă, mai ales în expresiile regulate mari. Doar introduceți-l în șablon grup nou captura, pe măsură ce numerotarea se pierde. Apoi va trebui să faceți corecții în toate locurile din program unde se accesează bufferele de captură după număr. Cu toate acestea, puteți asocia un nume cu un grup care vă permite să accesați tamponul corespunzător cu acel nume. Pentru a crea un grup numit, utilizați delimitatori ( ? ⋯) , unde numele dorit este înlocuit cu numele.

Părțile unui șir capturate în buffer-uri pot fi utilizate în două moduri. În primul rând, un program care utilizează o expresie regulată pentru a găsi sau înlocui se poate referi la buffer-uri ca variabile speciale. Această utilizare va fi discutată în secțiunea „Căutare și înlocuire operatori”. A doua posibilitate presupune utilizarea link-urilor către grupuri direct într-o expresie obișnuită, vezi secțiunea „Backlink-uri”.

Luați în considerare problema găsirii cuvintelor care conțin trei litere vocale identice la rând. Soluția naivă [aеооуеуя](3) folosind cuantificatori nu va funcționa, deoarece acest model se potrivește șiruri cu trei vocale consecutive, dar nu neapărat aceleași. Respingem cu indignare soluția monstruoasă cu o listă completă de alternative, aaa|eeee|yoyo|iii|oooo|uuu|eeee|yuyuyu|yayay: la urma urmei, merită să luăm o altă clasă simbolică, mai extinsă, sau să înlocuim triplul în cuantificator cu valoare mai mare, deoarece dimensiunea șablonului va crește catastrofal.

Tot posibil solutie eleganta, folosind grupuri de captură. Să captăm vocala într-un grup și apoi să facem referire la conținutul tamponului de capturare. Referințele la primul, al doilea, al treilea buffer sunt scrise în expresie regulată ca \g1 , \g2 , \g3 . Deci, soluția este modelul ([aeeioueyuya])\g1(2) . Vă rugăm să rețineți că referința la tamponul de captare trebuie să vină strict după grupul corespunzător din expresia regulată.

Backlink-uri se poate referi nu numai la tampoane numerotate, ci și la cele numite. Astfel de legături arată ca \k , unde, din nou, în loc de nume există un nume specific. Exemplul nostru poate fi rescris folosind grupuri numite: (? [aeeyoooeyya])\k {2} (vocală- vocală).

Uneori este nevoie de o căutare care să nu facă distincția între literele mici și mari. Această căutare se numește insensibil la majuscule (insensibil la majuscule). În loc să înlocuim literele peste tot în model cu clase de două litere (a → , b → , ...), pur și simplu includem modelul în grup special, activând modul de căutare fără majuscule: (? eu:⋯) . Un astfel de grup nu este un grup de captură. Dacă căutarea care nu ține seama de majuscule și minuscule trebuie implementată doar pe o parte a expresiei regulate, atunci numai partea necesară ar trebui plasată în grup.

Dimpotrivă, dacă o parte a expresiei regulate în care se efectuează căutarea fără diferențiere între majuscule și minuscule trebuie să dezactiveze acest mod, atunci puteți reveni la căutarea obișnuită, care diferențiază majuscule și minuscule folosind grupul ( ?-i: ⋯) .

Modurile sensibile la majuscule/minuscule afectează numai literele. Ceea ce contează ca o literă și ce nu depinde de limbă, la fel ca și regulile de potrivire între majuscule și litere mici. Din punctul de vedere al limbii engleze, de exemplu, simbolul Ш nu este o literă În limba germană există litera ß (apropo, versiunea majusculă a acestei litere este formată din două litere SS: Carl Friedrich Gauß. → CARL FRIEDRICH GAUSS).