PERL Laborator 6 Adrian Iftene Martie 26, 2007 1
1 Greşeli Comune când lucrăm cu RegExp... 3 2 Câteva Subiecte mai Avansate... 3 2.1 Comentarii Inline... 4 2.2 Modificatori Inline... 4 2.3 Gruparea fără Backreferences... 5 2.4 Căutări după şi înainte... 6 2.5 Backreferences (din nou)... 8 3 Concluzii... 9 4 Probleme de antrenament... 9 4.1 Modificatori Inline... 9 2
1 Greşeli Comune când lucrăm cu RegExp Sunt câteva greşeli comune pe care oamenii tind să le facă când lucrează cu expresii regulate. Am văzut că /a*b*c*/ se va potri pe orice şir, chiar dacă fiecare literă se potriveşte de zero ori. Ce altceva putem greşi? Să uităm să grupăm literele /Tam{2}/ se va potrivi cu Tamm, în timp ce /(Tam){2}/ se va potrivi cu TamTam, deci să fim atenńi atunci când alegem una din ele. Acelaş lucru pentru alternative: /Simplu pe/ se va potrivi cu Simplu şi cu pe, în timp ce /Sim(plu pe)/ se va potrivi cu fiecare din Simplu şi Simpe separat. Punerea greşită a ancorelor ^ se foloseşte la început, şi $ la sfârşit. Un dolar în orice altă parte din şir va conduce la încercarea de interpolare cu o variabilă. Să uităm să sărim peste caracterele speciale Dorim să le folosim cu înńelesul lor special? Trebuie să fim atenńi la următoarele caractere:. *? + [ ] ( ) { } ^ $ şi bineînńeles chiar \. Ne-numărarea de la zero Să nu uităm că intrarea într-un şir e dată de pozińia zero. Numărarea de la zero Cu toate că de regulă numărătoarea se face de la zero la backreferences începe de la $1. Asta deoarece Perl foloseşte o caracteristică dintr-un limbaj awk care foloseşte ca $1 referinńa către prima variabilă. 2 Câteva Subiecte mai Avansate Până acuma nu am intrat mai în adâncul sintaxei expresiilor regulate Perl-ul are obiceiul de a adăuga caracteristici neconvenńionale, chiar bizare uneori bazându-se pe anumite reguli. Toate extensiile încep cu un semn de întrebare într-un grup aceasta face să ne oprim şi să ne întrebăm: Chiar dorim să facem asta? Câteva din acestea sunt experimentale şi pot diferi de la o versiune la alta de Perl (şi e posibil ca ele să dispară la un moment dat). Câteva din ele sunt foarte utile şi le vom urmări în continuare. 3
2.1 Comentarii Inline Deja am văzut cum putem folosi modificatoul /x pentru a adăuga comentarii şi spańii albe expresiilor noastre regulate. Putem să facem asta cu şablonul (?#): /^Astazi (?# Aceasta parte este ignorata) suntem in data:/ Din păcate, nu avem nici o posibilitate să avem paranteze în interiorul acestor comentarii, şi Perl va închide comentariul la prima paranteză închisă. 2.2 Modificatori Inline Dacă citim şabloane dintr-un fişier sau le construim din interiorul codului nostru, nu avem nici o modalitate de a adăuga un modificator la sfârşitul operatorului expresiei regulate. De exemplu: #!/usr/bin/perl #inline.plx use warnings; use strict; my $sir = Avem mai mult de o Modalitate de a face asta! print Introduceti o expresie de test: ; my $sablon = <STDIN; chomp($sablon); if ($sir = ~ /$sablon/) { print Felicitari! $sablon se potriveste pe sir.\n ; } else { print Ne pare rau. Nici o potrivire pentru $sablon.\n ; } Dacă rulăm acest program şi uităm modul în care era capitalizat şirul nostru, vom obńine: perl inline.pl Introduceti o expresie de test: o modalitate de a face asta! Ne pare rau. Nici o potrivire pentru o modalitate de a face asta!. 4
Cum putem să facem aceasta să fie case-insensitive? SoluŃia este să folosim un modificator inline cu sintaxa (?i). Aceasta va face să avem potrivire case-insensitive. Prin urmare vom avea: perl inline.pl Introduceti o expresie de test: (?i)o modalitate de a face asta! Felicitari! (?i)o modalitate de a face asta! se potriveste pe sir. Dacă, invers, avem un modificator în locul în care dorim să scăpăm temporar de acest lucru, putem folosi, de exemplu, (?-i). Dacă avem: /Avem mai mult de o ((?-i)modalitate) de a face asta!/i; doar cuvântul Modalitate va fi potrivit case-insensitive. De notat că putem folosi de asemenea modificatorii: /m, /s şi /x în acelaşi mod. 2.3 Gruparea fără Backreferences Parantezele ajută în gruparea şi popularea variabilelor backreferences. Dacă avem o porńiune a potrivirii noastre în paranteze, aceasta va fi, în caz de succes, plasată în una din variabilele numerotate. Totuşi, e posibil ca în anumite situańii să dorim să folosim paranteze pentru grupare. De exemplu, ne aşteptăm ca prima backreference să conńină ceva important, dar putem să avem alt text în apropiere. Putem avea ceva după cum urmează: /(X-)?Topica: (\w+)/; Nu putem fi siguri dacă prima noastră backreference va fi în prima sau în a doua ($1 sau $2) asta va depinde de faptul că X- este prezentă sau nu. De exemplu, dacă avem şirul Topica: vremea, vom vedea că $1 este nedefinită. Dacă vom încerca să facem ceva cu variabila $1, vom primi un mesaj de avertizare deoarece variabila nu a fost inińializată: Use of uninitialized value in concatenation Aceasta nu este neaparat o problemă. După toate acestea vom găsi cuvântul nostru în $2 dacă avem ceva după Topica: bineînńeles. E suficient să fim atenńi să nu folosim variabila $1? Dar ce se întâmplă dacă avem mai mult de un câmp opńional? Să presupunem că avem o expresie cu 2 până la 6 grupuri opńionale. E posibil să avem în $2 primul nostru cuvânt şi de abia în $6 al doilea, în timp ce $1, $3, $4 şi $5 rămân nedefinite. Acesta desigur nu este un exemplu bun de programare şi conduce spre diferite probleme. Prin urmare, nu va trebui să folosim câmpuri backreference dacă nu este neaparată nevoie. Putem rezolva această problemă foarte uşor, prin adăugarea caracterelor?: ca mai jos: 5
/(?:X-)?Topica: (\w+)/; Aceasta ne va asigura că primul pereche de paranteze este doar un grup şi nu vom avea o valoare corespunzătoare într-o variabilă backreference. Cuvântul nostru va fi pus întotdeauna în $1. 2.4 Căutări după şi înainte Uneori e posibil să dorim ca pe o anumită linie să înlocuim cuvântul peste cu tort, dar numai dacă următorul cuvânt este frisca. Putem să facem asta foarte simplu, spunând: s/peste frisca/tort frisca/ Ce se întâmplă? Motorul expresiilor regulate caută şirul referinńă, cautând peste frisca. Dacă găseşte unul, îl va înlocui cu textul tort frisca. Am obńinut ceea ce doream. În acest caz nu e aşa complicat să înlocuim şapte caractere din fiecare potrivire cu alte şapte caractere identice (ne referim la partea comuna frisca ). Nu e greu de văzut că această metodă este ineficientă şi poate produce adevărate probleme într-un program în care folosim excesiv substituńia. Ce dorim să facem: să găsim o cale să precizăm într-o potrivire avem potrivirea textului numai dacă cuvântul următor este frisca. Având potrivire pe cuvântul peste, vom căuta după să vedem dacă avem frisca în continuare (şi avem potrivire dacă acesta există, şi vom înlocui cu tort ), dacă nu mergem mai departe fără a mai face înlocuirea. În Perl acest lucru este foarte uşor, deoarece avem un operator special pentru astfel de lucruri: /peste (?= frisca)/ care face exact ce dorim se uită după peste, după care cauta frisca în continuare, şi returnează potrivire numai dacă această a doua potrivire reuşeşte de asemenea. De exemplu: #!/usr/bin/perl #cautare.plx use warnings; use strict; $_ = peste frisca si peste marinat ; print Comanda noastra initiala a fost, $_, \n ; s/peste(?= frisca)/tort/; print Acuma, am schimbat-o in, $_, \n ; 6
ne va returna: perl cautare.pl Comanda noastra initiala a fost peste frisca si peste marinat Acuma, am schimbat-o in tort frisca si peste marinat Putem de asemenea să ne uităm să nu urmeze ceva anume, folosind un semnul mirării în locul semnului de egalitate: /peste(?! frisca)/ care va întoarce potrivire pentru peste doar dacă cuvântul următor nu este frisca. Dacă vom modifica programul de mai sus după cum urmează: #!/usr/bin/perl #cautare2.plx use warnings; use strict; $_ = peste frisca si peste marinat ; print Comanda noastra initiala a fost, $_, \n ; s/peste(?! frisca)/tort/; print Acuma, am schimbat-o in, $_, \n ; ne va returna: perl cautare2.pl Comanda noastra initiala a fost peste frisca si peste marinat Acuma, am schimbat-o in peste frisca si tort marinat Căutările înainte sunt foarte puternice mai ales dacă nu vom folosi anumite expresii specifice (folosind metacaracterele) cu ele. Analog, putem să căutăm un text ce precede un anumit şablon. Avem o pereche similară de operatori ce caută înainte. Vom folosi semnul < care caută înaintea potrivirii, potrivirea frisca doar dacă avem peste înaintea lui. Astfel pentru a căuta expresia de mai sus, vom folosi: /(?<=peste) frisca/ iar pentru a căuta toate torturile sau prăjiturile cu frisca : /(?<!peste) frisca/ 7
Hai să înlocuim cu peste si cartofi în loc de peste frisca si tort ciocolata în loc de tort frisca : Dacă vom modifica programul de mai sus după cum urmează: #!/usr/bin/perl #cautare3.plx use warnings; use strict; $_ = peste frisca si tort frisca ; print Comanda noastra initiala a fost, $_, \n ; s/(?<=peste) frisca/si cartofi/; print Acuma, am schimbat-o in, $_, \n ; s/(?<!peste) frisca/ciocolata/; print In final comanda este, $_, \n ; ne va returna: perl cautare3.pl Comanda noastra initiala a fost peste frisca si tort frisca Acuma, am schimbat-o in peste si cartofi si tort frisca In final comanda este peste si cartofi si tort ciocolata 2.5 Backreferences (din nou) Să ne uităm din nou la backreferences. Să presupunem că dorim să găsim orice cuvinte care se repetă. Cum putem face asta? Să încercăm să facem astfel: if (/\b(\w+) $1\b/) { print Cuvantul care se repeta: $1\n ; } Dar, vom vedea că aceasta nu funcńionează, deoarece $1 este obńinut doar după ce s-a terminat potrivirea. Vom vedea că, avem un mesaj de avertizare, care ne va spune că variabila $1 nu este definită. Pentru a face potriviri în timp ce suntem în timpul unei expresii regulate, vom folosi următoarea sintaxă: if (/\b(\w+) \1\b/) { print Cuvantul care se repeta: $1\n ; } 8
3 Concluzii Expresiile regulate ne oferă o modalitate foarte puternică atunci când căutăm şabloane întrun text, extrag bucăńi din text şi fac înlocuiri de text. Am văzut cum putem găsi potriviri simple, şi cum să ne referim mai apoi la bucăńi de potrivire, cum să înlocuim text şi chiar săl transformăm. Cheia învăńării şi înńelegerii expresiilor regulate este să fim capabili să împărńim în părńi componente pe care mai apoi să le procesăm. Odată ce începem să înńelegem scopul expresiilor regulate, vom fi în stare să creem potriviri complexe care să ne ajute. 4 Probleme de antrenament 4.1 Modificatori Inline Folosind nońiunile din acest laborator, realizańi un program care să conńină următoarele: Căutare case-insensitive doar pe anumite zone de text; Înlocuiri dacă şablonul respectate anumite condińii: urmează sau precede un anumit text; Coincide numărul de codificări cu numărul de decodificări? 9