3
Cercul de programare .NET ADF 1
O expresie regulată este un set de caractere ce pot fi comparate cu un alt set de caractere pentru a determina daca setul respectiv respectă un anumit format. Expresiile regulate pot fi folosite pentru: a determina daca un text respecta un anumit format (pattern matching) a extrage informaţii din un text (ex: extragerea numarului de telefon al unui utilizator din o pagina html) a reformata text şi a elimina caracterele invalide. 2
Vom folosi metoda statica IsMatch din clasa RegEx Ex: if (RegEx.IsMatch(@“^\d{5}$”,”1234”)) Console.WriteLine(“Avem match”); else Console.WriteLine(“NU avem match”); Problema mare este deci constructia expresiei regulate. 3
^ \d {5} $ Match pt inceput de input
Match pentru final de input Match numai pentru cifre
Match pentru exact 5 caractere
4
Pentru expresia regulata de mai sus vom avea match numai pentru siruri de 5 cifre: 12345 75123, dar nu pentru avcxc12345. Daca scoatem ^ atunci vom avea match si pentru avcxc12345, deci pentru siruri de text care se termina cu 12345. Expresiile regulate pot verifica text mult mai complicat, deci putem aveam pattern-uri mult mai complexe. 5
Concluzie: Expresiile regulate sunt dificil de creat, daca nu sunteti familiarizati cu formatul Creare de expresii regulate poate sa fie confuza. Citirea sigur este.
6
Cea mai simpla utilizate a expresieri regulate consta in verificarea daca un text contine alt text. Ex: avem textul abc si putem verifica daca acesta este continut in yasaabc, yhasabcadas. Pentru a verifica daca textul de cautat apare la inceputul textului in care cautam introducem ^ la inceputul expresiei regulate. Pentru a verifica daca textul de cautat apare la final introducem $ la finalul expresiei regulate. Cand cautam cuvine vom folosi \b iar cand ca textul nostru sa nu fie cuvant \B. 7
Ex: abc$ -> match “abc”, “dabc” dar nu “abcd” ^abc$ -> match “abc”, dar nu “dabc” si nici “abcd” mere\b-> match “mere” dar nu “merele” in textul “ana are mere si merele sunt coapte” mere\B-> match “merele” dar nu “mere” in acelasi text
8
Caracter
Descriere
^
Matchul trebuie sa inceapa ori la primul caracter din string, ori la primul de pe fiecare linie.
$
Matchul trebuie sa se termine la ultimul caracter din string sau la ultimul caracter de pe fiecare linie.
\A
Matchul trebuie sa inceapa de la primul caracater din string (ignora textul pe mai multe linii)
\Z
Matchul trebuie sa se termine ori la ultimul caracter din string ori inainte de \n la finalul stringului (ignora liniile multiple)
\z
Matchul trebuie sa se termine ori la ultimul caracter din string
\G
Matchul trebuie sa inceapa unde matchul anterior s-a terminat.
\b
Matchul trebuie sa aiba loc la granita intre cuvine
\B
Invers decat la \b. 9
Pentru match caractere speciale:
\t -> tab \r -> carrige return \v -> tab vertical \n -> linie noua \e -> escape \040 -> pt caracatere ASCII. Ex: 40 in ASCII inseamna spatiu. \x20 -> pt caracater ASCII, folosind reprezentarea hex \cC -> pt caractere de control ASCII. \cC == control + C \* -> asterix \\ -> backslash 10
“*” match atunci cand caracterul anterior lui * se repata de 0 sau mai multe ori.Ex: Pentru “to*n” vom avea match pentru “tooon”, “tn” si “ton” “+” match atunci cand caracterul anterior lui * se repata CEL PUTIN o data.Ex: Pentru “to+n” vom avea match pentru “tooon”, si “ton” dar nu pentru “tn” Pentru a avea match pentru un numar fix de repetari al unui caracater vom folosi {}. Ex: “to{3}n” -> match pentru “tooon” dar nu pentru “tn” sau “ton” Intre {} putem pune si un range de aparitii pentu care vom avea match: {1,2}. Vom cauta match pentru cel putin o aparatie si cel mult 2. Ex: “to{1,2}n” -> match pentru “ton” si pentru”toon” dar nu pentru “tn” sau “tooon”. Daca folosim “{1,}” vom cauta minim 1 aparatie. 11
Pentru a face un simbol optional: “?”. Ex: “to?n” -> vom avea match pentru “ton” si pentru “tn”, dar nu pentru “tooon”. Pentru match simbol optional dar o singura aparite (deci obligatoriu o aparitie): “.”. Ex: “to.n” -> match: “toqn”, “tojn”, “toon” dar nu “ton” sau “tn”. Pentru match un caracater din mai multe posibile: [caractere]. Ex: “to[ro]n” -> match “toon” si “torn” dar nu “ton” sau “toron”. Pentru match un range de caractere: [caracater-caracater]. Ex: “to[p-s]n” -> match “topn”, “toqn”, “torn”, “tosn” si atat.
12
Pentru match un caracter sau altul: caracater|caracter. Ex: “z|food” -> match “z” si “food”. “(z|f)ood” -> match “zood” si “food”. Pentru rangeruile obisnuite putem folosii urmatoarele:
\d -> numere. Echivalent “[0-9]”. \D -> non numere. Echivalent “[^0-9]”. \s -> orice caracter alb (spatiu, tab, etc.) \S -> invers \s \w -> orice caracter din un cuvant. Echivalent [A-Za-z0-9_]. \W -> invers \w. 13
Pentru match grupuri de caractere vom pune grupul de caractere intre paranteze rotunde. Ex: “foo(loo){1,3}hoo” -> match “fooloohoo” sau “foolooloohoo” sau “fooloolooloohoo”
14
Prin backreferencing (referentiere inapoi) putem cauta aparitii multiple ale unui anumit grup de caractere. Gaseste cuvinte care contin caractere adiacente identice: coffee. I’ll in “I’ll have coffee”
(?
\w) \k Forteaza motorul de expresii regulate sa tina minte match-ul unei subexpresii,in acest caz un singur caracter
Cautam in cuvinte Forteaza motorul de expresie regulata sa compare acutalul caracter cu fostul match stocat in “char” 15
Pentru a gasi cuvine care se repeta: inlocuim \w cu \w+ si adaugam \s pentru a obtine match atunci cand avem un spatiu inaintea cuvantului. Expresia regulata devine “(? \s\w+)\k”. Exista o problema. Expresia regulata nu da match numai pentru “ the the” dar si pentru “ the theory”. Solutia este sa adaugam un \b la final: “(? \s\w+)\k\b”. 16
O referina inapoi se va referi numai la cel mai recent match, deci la cea mai recenta definitie a unui grup.
17
Optiuni in expresii regulate La construirea expresiei regulate putem specifica mai mult optiuni (ex. IgnoreCase). Vom lega optinile prin operatorul SAU pe biti. Ex: IgnoreCase | MultiLine. Unele optiuni pot fi specificate in textul expresiei regulate. Pentru mai multe detalii despre optiuni: vedeti in carte la pagina 156. 18
Extragerea datelor pentru care s-a realizat match: PAS1: crearea expresiei regulate PAS2: Crearea unei instante a clasei System.Text.RegularExpressions.Match, folosind metoda statica Regex.Match PAS3: Obtinerea match-urile prin accesarea array-ului de elemente Match.Groups 19
ex: string input = “Oras: Iasi”; Match m = Regex.Match(input, @”Oras : (.*$)”); Console.WriteLine(m.Groups[1]); sau string input= "http://www.yahoo.com/" Regex exp = new Regex(@" http://(www\.)?([^\.]+)\.com", RegexOptions.IgnoreCase); Match m = exp.Match(input); 20
Putem itera prin match-uri: Match m; string input = "http://www.yahoo.com/index.html si http://www.google.com/"; Regex exp = new Regex(@"http://(www\.)?([^\.]+)\.com"); for (m = exp.Match(input);m.Success;m = m.NextMatch()) { Console.WriteLine("link la:" + m.Groups[0]); } 21
Putem face reformatari
Match m; string input = "http://www.yahoo.com/index.html si http://www.google.com/"; Regex exp = new Regex(@"http://(www\.)?(?([^\.]+))\.com"); for (m = exp.Match(input); m.Success; m = m.NextMatch()) { Console.WriteLine("link la:" + m.Result(“${numeSite}”)); } 22
Inlocuirea de text folosint RegEx:
ex1: string MDYtoDMY(string input) { return Regex.Replace(input,@ “\b(?\d{1,2})/(?\d{1,2})/(?\\d{2,4})\b”, “${zi}-${luna}-${an}) } In acest exemplu schimbam din luna/zi/an in zi-luna-an 23
ex2: Curatare adresa de email Regex.Replace(@“[^\w\.@-]”,””); Constructii acceptate in meteoda Replace:
${nume}: inlocuieste ultimul substring cu numele nume $numar: inlocuieste ultimul substring cu numarul numar $$: inlocuieste un singur $ literal $&: inlocuieste o copie a intregului match $`: inlocuieste tot textul de la inceputul stringului pana la gasirea match-ului $’: inlocuieste tot textul de la mathc pana la sfarsitul stringului $+: inlocuieste ultimul grup gasit $_: inlocuieste intregul string de intrare 24
In general platforma .Net se ocupa ea singura de econdare/decodare. Cazurile in care trebuie sa modificam noi encodarea/decodare sunt:
dorim interoperatibilitate cu UNIX citire si fisere text din alte limbi crearea de pagini HTML generam manual mesaje email 25
Econdarea este operatia prin care asignam unui caracter text un numar. Decodare este operatie inversa Cel mai cunoscut cod este codul ASCII (American Standard Code for Information Interchange) Codul ASCII asigneaza caracterelor text numere pe 7 biti (deci de la 0 la 127 inclusiv). Ex: 0x43 pentru “C” 26
In timp ce codul ASCII are destule numere pentru caracterele latine, nu are destul pentru celelalte (non-latine). In timp producatorii au incepetut sa foloseasca celelalte 128 de numere (de la 128 la 225) pentru ca codifica caracactere non-latine. S-a ajuns ca mai multe caractere sa ocupe acceasi pozitie in codul ASCII. Pentru a reduce problemele ANSI a definit pagini de cod standard ASCII (code pages), care au caractere latine de la 0 la 127 si caractere specifice de la 128 la 255. Ex: ISO-8859-1: code page 28591: “Western European ISO” 27
Daca ati vazut vreodata o pagina web, un email sau o subtitrare in care apareau dreptunghiuri sau semne de intrebare in locul caracterelor, acolo era o problema de encodare. Din ce in ce mai mult codul ASCII si paginile de cod ASCII sunt inlocuite de UNICODE. Unicode este o pagina de cod masiva ce suporta caractere din majoritatea limbilor si scrisurilor. Ex: alfabetul Latin, Grecesc, Chinezesc , etc. 28
Unicode nu contine o encodare propriuzisa, existand mai multe implementari. .NET framework foloseste Unicode UTF-16 pentru a reprezenta caractere si uneorie UTF-8 pentru lucrul intern. System.Text contine mai multe clase prin care sunt suportate urmatoarele encodari:
Unicode UTF32 Unicode UTF16 Unicode UTF8 ASCII ANSI/ISO 29
Unicode UTF-32: Caractere unicode ca secvente de 32 de biti. Folositi clasa UTF32Encoding pentru encodare, decodare Unicode UTF-16: Secvente de 16 biti. Folositi clasa UnicodeEncoding pentru encodare, decodare. Unicode UTF-8: Foloseste secventa de 8,16,24 pana la 48 biti pentru encodare. Valorile 0-127 sunt exact cele de la ASCII. Valorile de la 128 la 2047 folosesc encodare pe 16 biti si ofera suport pentru Latina, Greaca, Chirilica, Ebraica si Arabica. Valorile de la 2048 la 65536 folosesc encodare pe 24 biti pentru a oferi suport de Coreana, Chineza, Japoneza si alte limbi care au nevoie de un numar mare de caractere. Puteti folosi clasa UTF8Encoding pentru encodare/decodare. ASCII: Encodeaza alfabetul latin in caractere de 7 biti. Este inadecvat pentru aplicatii internationalizate. Folositi clasa ASCIIEncoding pentru encodare ASCII. ASNI/ISO: Avem la dispozitie o gama larga de encodari ANSI/ISO 30
Puteti folosi System.Text.Encoding.GetEncoding pentru a returna obiectul pentru o anumita encodare. Puteti folosi Encoding.GetBytes pentru a convertii un string unicode in reprezentarea sa in bytes. Exemplu: Encoding e = Encoding.GetEncoding(“Korean”); byte[] encoded; encoded = e.GetBytes(“Salutari”); for (int i=0;i<e.Lenght;i++) Console.WriteLine(“Byte {0}: {1}”,i,encoded[i]); 31
Codul de mai sus arata cum sa convertim un text din ASCII in o alta encodare. Totusi in mod normal nu facem o astfel de conversie, adica nu schimbam encodarea unui text ce foloseste numai caractere latine. In majoritatea code page-urilor primele 128 de numere sunt identice cu cele ASCII In concluzie chiar daca am trecut pe encodare in Coreana, textul nostru va arata la fel la scriere in un fisier (de exemplu) 32
EncodingInfo[] encodari=Encoding.GetEncodings(); foreach(EncodingInfo e in encondari) { Console.Writeline(e.CodePage + “ “+ e.Name + “ “+ e.DisplayName); } 33
StreamWriter sw = new StreamWriter(“file.txt”,False, Encoding.UTF8); sau UTF32,Unicode. by default .net alege UTF16
34
In general nu trebuie specificata encodarea deoarece .net framework o va gasii singur. Totusi cand lucram cu UTF7 aceasta trebuie specificata: StreamReader sr = new StreamReader (“file.txt”, Encoding.UTF7);
35
36