Expresii regulate
Expresiile regulate reprezintă un instrument puternic pentru procesarea șirurilor de caractere. Ele permit definirea unui șablon la care trebuie să corespundă un șir sau un subșir.
Unele metode din clasa String acceptă expresii regulate și le folosesc pentru a efectua operațiuni pe șiruri.
Metoda split()
Pentru a împărți un șir în subșiruri se folosește metoda split(). Aceasta poate accepta o expresie regulată ca parametru, care definește criteriul de împărțire a șirului.
De exemplu, împărțim o propoziție în cuvinte:
String text = "FIFA will never regret it";
String[] words = text.split("\\s*(\\s|,|!|\\.)\\s*");
for(String word : words){
System.out.println(word);
}
Pentru împărțire se folosește expresia regulată \\s*(\\s|,|!|\\.)\\s*. Subexpresia \\s reprezintă un spațiu. Asteriscul indică faptul că simbolul poate apărea de la 0 până la un număr infinit de ori. Prin urmare, \\s* înseamnă un număr nedefinit de spații consecutive – adică nu contează câte spații sunt între cuvinte.
În plus, pot să nu existe deloc spații. În paranteze este indicat un grup de expresii care poate urma după un număr nedefinit de spații. Grupul ne permite să definim un set de valori separate printr-o bară verticală, și subșirul trebuie să corespundă uneia dintre aceste valori.
În grupul \\s|,|!|\\. subșirul poate fi un spațiu, o virgulă, un semn de exclamare sau un punct. Deoarece punctul are un sens special în expresiile regulate, trebuie să îl scăpăm cu ajutorul unei bare inverse.
Potrivirea unui șir. matches
O altă metodă a clasei String, matches(), acceptă o expresie regulată și returnează true dacă șirul corespunde acesteia. În caz contrar, returnează false.
De exemplu, verificăm dacă un șir corespunde unui număr de telefon:
String input = "+12343454556";
boolean result = input.matches("(\\+*)\\d{11}");
if(result){
System.out.println("Este un număr de telefon");
}
else{
System.out.println("Nu este un număr de telefon!");
}
În acest caz, expresia regulată definește mai întâi un grup (\\+*), adică șirul poate începe cu semnul plus, dar acesta poate lipsi. Apoi, verificăm dacă următoarele 11 caractere sunt cifre.
Expresia \\d reprezintă un caracter numeric, iar numărul din acolade {11} indică de câte ori trebuie să se repete acest tip de caractere. Astfel, căutăm un șir care poate începe cu semnul plus (sau nu) și urmat de 11 cifre.
Clasa Pattern
Cea mai mare parte a funcționalității pentru lucrul cu expresii regulate în Java este concentrată în pachetul java.util.regex.
Expresia regulată în sine reprezintă un șablon pentru căutarea potrivirilor într-un șir. Pentru a defini un astfel de șablon și pentru a căuta subșiruri care corespund acestuia, în Java sunt definite clasele Pattern și Matcher.
Pentru o căutare simplă a potrivirilor, clasa Pattern definește metoda statică boolean matches(String pattern, CharSequence input). Aceasta returnează true dacă secvența de caractere input corespunde complet șablonului specificat:
import java.util.regex.Pattern;
public class StringsApp {
public static void main(String[] args) {
String input = "Hello";
boolean found = Pattern.matches("Hello", input);
if(found)
System.out.println("Găsit");
else
System.out.println("Nu a fost găsit");
}
}
Totuși, de obicei, pentru căutarea potrivirilor se folosește clasa Matcher.
Clasa Matcher
Principalele metode ale clasei Matcher sunt:
- boolean matches(): returnează true dacă întregul șir corespunde șablonului
- boolean find(): returnează true dacă există un subșir care corespunde șablonului și trece la acest subșir
- String group(): returnează subșirul care corespunde șablonului în urma apelării metodei find. Dacă nu există potriviri, metoda generează o excepție IllegalStateException
- int start(): returnează indexul potrivirii curente
- int end(): returnează indexul următor după potrivirea curentă
- String replaceAll(String str): înlocuiește toate potrivirile cu subșirul specificat și returnează șirul modificat
Să folosim clasa Matcher. Mai întâi trebuie să creăm un obiect Pattern folosind metoda statică compile(), care permite setarea șablonului:
Pattern pattern = Pattern.compile("Hello");
Ca șablon este folosit șirul "Hello". Metoda compile() returnează un obiect Pattern pe care îl putem folosi în program.
În clasa Pattern este definită și metoda matcher(String input), care primește șirul în care se caută potrivirea și returnează un obiect Matcher:
String input = "Hello world! Hello Java!";
Pattern pattern = Pattern.compile("hello");
Matcher matcher = pattern.matcher(input);
Apoi, pentru a căuta potriviri, se apelează metoda matches() a obiectului Matcher:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringsApp {
public static void main(String[] args) {
String input = "Hello";
Pattern pattern = Pattern.compile("Hello");
Matcher matcher = pattern.matcher(input);
boolean found = matcher.matches();
if(found)
System.out.println("Găsit");
else
System.out.println("Nu a fost găsit");
}
}
Un exemplu mai funcțional, care caută potriviri parțiale într-un șir:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringsApp {
public static void main(String[] args) {
String input = "Hello Java! Hello JavaScript! JavaSE 8.";
Pattern pattern = Pattern.compile("Java(\\w*)");
Matcher matcher = pattern.matcher(input);
while(matcher.find())
System.out.println(matcher.group());
}
}
Să presupunem că dorim să găsim în șir toate aparițiile cuvântului "Java". În șirul sursă, aceste apariții sunt: "Java", "JavaScript" și "JavaSE". Pentru aceasta, folosim șablonul "Java(\\w*)". Acest șablon specifică faptul că toate potrivirile trebuie să înceapă cu "Java", iar expresia (\\w*) permite ca după "Java" să apară orice număr de caractere alfanumerice.
Expresia \\w reprezintă un caracter alfanumeric, iar asteriscul indică un număr indefinit de astfel de caractere. Pentru a ne asigura că Java nu interpretează \\w ca o secvență de escape, folosim o altă bară inversă pentru a o scăpa.
Apoi, folosim metoda find() a clasei Matcher pentru a trece la următoarea potrivire în șir. Fiecare apel al metodei va găsi următoarea potrivire. Cu ajutorul ciclului while(matcher.find()), putem parcurge toate potrivirile. Fiecare potrivire poate fi obținută cu metoda matcher.group(). Rezultatul va fi:
Java
JavaScript
JavaSE
Înlocuirea într-un șir
Acum vom înlocui toate potrivirile folosind metoda replaceAll():
String input = "Hello Java! Hello JavaScript! JavaSE 8.";
Pattern pattern = Pattern.compile("Java(\\w*)");
Matcher matcher = pattern.matcher(input);
String newStr = matcher.replaceAll("HTML");
System.out.println(newStr); // Hello HTML! Hello HTML! HTML 8.
De asemenea, clasa String are o metodă replaceAll() cu funcționalitate similară:
String input = "Hello Java! Hello JavaScript! JavaSE 8.";
String myStr = input.replaceAll("Java(\\w*)", "HTML");
System.out.println(myStr); // Hello HTML! Hello HTML! HTML 8.
Împărțirea șirului în lexeme
Cu ajutorul metodei String[] split(CharSequence input) din clasa Pattern putem împărți un șir într-un tablou de subșiruri pe baza unui separator. De exemplu, putem extrage cuvintele dintr-un șir:
import java.util.regex.Pattern;
public class StringsApp {
public static void main(String[] args) {
String input = "Hello Java! Hello JavaScript! JavaSE 8.";
Pattern pattern = Pattern.compile("[ ,.!?]");
String[] words = pattern.split(input);
for(String word : words)
System.out.println(word);
}
}
Și rezultatul va fi:
Hello
Java
Hello
JavaScript
JavaSE
8
În acest proces, toți separatorii sunt eliminați. Totuși, această metodă nu este perfectă, deoarece rămân unele spații care sunt considerate lexeme. Pentru o împărțire mai precisă, ar trebui să folosim elementele expresiilor regulate. Astfel, putem înlocui șablonul cu următorul:
Pattern pattern = Pattern.compile("\\s*(\\s|,|!|\\.)\\s*");
Acum vor rămâne doar cuvintele:
Hello
Java
Hello
JavaScript
JavaSE
8