1 of 22

User Interfaces Design

Java - Espressioni Regolari e Streams

Carmine Dodaro - Università della Calabria

2 of 22

Espressioni Regolari

3 of 22

Espressioni regolari

  • Un’espressione regolare è una stringa di testo che può essere utilizzata per trovare dei pattern all’interno di un’altra stringa.

  • La classe di riferimento per le espressioni regolari in Java è la classe Pattern che si trova nel package java.util.regex.

  • La classe Pattern si può usare in due modi:
    • usando il metodo statico matches, che restituisce true se input soddisfa l’espressione regolare, false altrimenti.
    • usando il metodo static compile, che permette di compilare un’espressione regolare e poi utilizzarla più volte.

4 of 22

Metodi matches e compile

public boolean testMatches(String regex, String input) {

boolean res = Pattern.matches(regex, input);

if(res)

System.out.println("Espressione regolare soddisfatta");

else

System.out.println("Espressione regolare non soddisfatta");

}

public boolean testCompile(String regex, String input) {

Pattern pattern = Pattern.compile(regex);

Matcher matcher = pattern.matcher(input);

boolean res = matcher.matches();

if(res)

System.out.println("Espressione regolare soddisfatta");

else

System.out.println("Espressione regolare non soddisfatta");

}

5 of 22

Espressioni regolari: caratteri

\n

il carattere per indicare una nuova linea

\t

il carattere tab

\\

il carattere per indicare il backslash

.

qualunque carattere tranne il terminatore di linea

\d

una cifra (da 0 a 9)

\D

tutti i caratteri che non rappresentano una cifra

\s

uno spazio bianco

\S

un carattere che non è uno spazio bianco

6 of 22

Espressioni regolari: classi di caratteri

[abc]

il carattere a, b, oppure c

[^abc]

tutti i caratteri tranne a, b, oppure c

[a-zA-Z]

tutti i caratteri da a fino a z, sia minuscolo che maiuscolo

[a-d[m-p]]

tutti i caratteri da a fino a d, o i caratteri da m fino a p (unione)

[a-z&&[def]]

d, e, oppure f (intersezione)

[a-z&&[^bc]]

da a fino a z, eccetto b e c (sottrazione)

[a-z&&[^m-p]]

da a fino a z, eccetto i caratteri da m a p (sottrazione)

7 of 22

Espressioni regolari: quantificatori

X*

X si ripete zero o più volte

X+

X si ripete una o più volte

X?

X è presente zero o una volta

X{n}

X si ripete esattamente n volte

X{n,}

X si ripete almeno n volte

X{n,m}

X si ripete almeno n volte e al massimo m volte

8 of 22

Espressioni regolari: gruppi

L’uso dei gruppi permette di trattare un insieme di caratteri come se fossero un’unità. I gruppi si possono specificare mettendo tra parentesi tonda un insieme di caratteri da raggruppare.

I gruppi catturati si possono enumerare contando le parentesi aperte da sinistra a destra. Ad esempio, consideriamo l’espressione ((X1)(X2(X3))). In questo caso abbiamo 4 gruppi:

((X1)(X2(X3)))

(X1)

(X2(X3))

(X3)

Il metodo groupCount() della classe Matcher ci restituisce il numero di gruppi, mentre il metodo group(int n) ci restituisce la stringa associata all’ennesimo gruppo catturato.

All’interno dei gruppi si possono usare anche espressioni come | per indicare l’OR tra gli elementi del gruppo.

9 of 22

Esempi

Controlla se la stringa in input contiene una sequenza (non vuota) di a seguita da una b (es. aaaaaab).

public boolean test(String input) { return Pattern.matches("a+b", input); }

Controlla se la stringa in input contiene una sequenza (anche vuota) di a seguita da una b (es. aaaaaab).

public boolean test(String input) { return Pattern.matches("a*b", input); }

Controlla se la stringa in input contiene una sequenza (non vuota) di a seguita da una sequenza (non vuota) di b (es. aaabbbbb).

public boolean test(String input) { return Pattern.matches("a+b+", input); }

Controlla se la stringa in input contiene una sequenza (non vuota) di ab (es. ababab).

public boolean test(String input) { return Pattern.matches("(ab)+", input); }

10 of 22

Esempi

Controlla se la stringa in input contiene una sequenza (non vuota) di a seguita da una sequenza (non vuota) di b e c (es. aaabbbcccbcbcbcb).

public boolean test(String input){ return Pattern.matches("a+[bc]+", input); }

oppure

public boolean test(String input){ return Pattern.matches("a+(b|c)+", input); }

Controlla se la stringa in input contiene una sequenza (non vuota) di a seguita da una sequenza (non vuota) di b oppure di c (es. aaabbbbbb oppure aaaaccccc).

public boolean test(String input){ return Pattern.matches("a+(b+|c+)", input);}

Restituiamo true se la stringa in input contiene una sequenza (non vuota) di qualunque carattere tranne a (es. bbbbcsd).

public boolean test(String input){ return Pattern.matches("[^a]+", input);}

11 of 22

Esempi

Controlla se la stringa in input è composta da esattamente 5 a.

public boolean test(String input){ return Pattern.matches("a{5}", input); }

oppure

public boolean test(String input){ int n=5; return Pattern.matches("a{"+n+"}", input); }

Controlla se la stringa in input è composta da 5 caratteri diversi da a.

public boolean test(String input) { return Pattern.matches("[^a]{5}", input); }

12 of 22

Esempi

Controlla se la stringa in input è una sequenza (non vuota) di lettere minuscole.

public boolean test(String input){ return Pattern.matches("[a−z]+", input); }

Controlla se la stringa in input è una sequenza (non vuota) di lettere maiuscole.

public boolean test(String input){ return Pattern.matches("[A−Z]+", input); }

Controlla se la stringa in input è una sequenza (non vuota) di lettere minuscole e maiuscole.

public boolean test(String input){ return Pattern.matches("[a−zA−Z]+", input); }

Controlla se la stringa in input è una sequenza (non vuota) di lettere minuscole comprese tra la lettera a e la lettera d, oppure tra la lettera f e la lettera h.

public boolean test(String input){ return Pattern.matches("[a−d[f−h]]+", input); }

Controlla se la stringa in input è una sequenza (non vuota) di lettere minuscole tra a e z tranne p e q.

public boolean test(String input){ return Pattern.matches("[a−z&&[^pq]]+", input); }

Controlla se la stringa in input è una sequenza (non vuota) di lettere minuscole tra a e z tranne quelle tra p e t.

public boolean test(String input){ return Pattern.matches("[a−z&&[^p−t]]+", input); }

13 of 22

Esempi

Controlla se la stringa è un indirizzo email dei docenti unical (per semplicità assumiamo che gli indirizzi email possano contenere solo lettere)

public boolean testEmail(String input) {

return Pattern.matches("[a−zA−Z]+\\.[a−zA−Z]+@unical\\.it", input);

}

Controlla se la stringa è una dichiarazione di una variabile intera in Java

public boolean testDichiarazioneVariableInteraJava(String input) {

return Pattern.matches("int [a−zA−Z_][a−zA−Z_0−9]∗;", input);

}

Controlla se il codice fiscale è valido

public boolean testCodiceFiscale(String input) {

return Pattern.matches("[a−zA−Z]{3}[a−zA−Z]{3}\\d{2}[a−ehlmpr−tA−EHLMPR−T]\\d{2}[a−zA−Z]\\d{3}[a−zA−Z]", input);

}

14 of 22

Gruppi

E se volessimo stampare i vari componenti del codice fiscale?

public void test(String codiceFiscale) {

Pattern pattern = Pattern.compile("([a−zA−Z]{3})([a−zA−Z]{3})(\\d{2})([a−ehlmpr−tA−EHLMPR−T])(\\d{2})([a−zA−Z]\\d{3})([a−zA−Z])");

Matcher matcher = pattern.matcher(codiceFiscale);

if(matcher.matches()) {

System.out.println("cognome: " + matcher.group(1));

System.out.println("nome: " + matcher.group(2));

System.out.println("anno: " + matcher.group(3));

System.out.println("mese: " + matcher.group(4));

System.out.println("giorno: " + matcher.group(5));

System.out.println("codice comune: " + matcher.group(6));

System.out.println("codice controllo: " + matcher.group(7));

}

else

System.out.println("Codice fiscale non valido");

}

15 of 22

Espressioni regolari e stringhe

Attenzione: non tutti i metodi interpretano espressioni regolari! Ad esempio il metodo replace riceve come parametro una stringa.

String testo = "2021−03−12_18:22:30";

String data = testo.replaceAll("_.*", "");

System.out.println(data);

Risultato della stampa: 2021−03−12

String testo = "1;nome;cognome;data di nascita";

String[] result = testo.split(";");

System.out.println(result[0] + " " + result[1] + " " + result[2] + " " + result[3]);

Risultato della stampa: 1 nome cognome data di nascita

16 of 22

Streams

17 of 22

Streams

Java permette di utilizzare degli Stream, che possono essere visti come una sequenza di elementi a partire da una sorgente.

Sono molto utili per scrivere del codice in modo più compatto e con meno rischio di errori. Non vediamo una spiegazione teorica degli Stream ma facciamo qualche esempio pratico e confronto con codice tradizionale per capirne le potenzialità.

18 of 22

Esempi

//Stampa di una lista di interi

public void test() {

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

//Stampa della lista con il for

for(Integer i : l) {

System.out.println(i);

}

//Stampa della lista usando lo stream – Metodo 1

l.stream().forEach(System.out::println);

//Stampa della lista usando lo stream - Metodo 2

//L’espressione x -> System.out.println(x) è una lambda expression

l.stream().forEach(x -> System.out.println(x));

}

19 of 22

Esempi

//Stampa dei numeri negativi all’interno di una lista di interi

public void test() {

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

for(Integer i : l) {

if(i < 0)

System.out.println(i);

}

l.stream().filter(x -> x < 0).forEach(System.out::println);

}

//Salvataggio dei numeri negativi in una nuova lista

public void test() {

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

List<Integer> res = new ArrayList<Integer>();

for(Integer i : l) {

if(i < 0)

res.add(i);

}

List<Integer> res2 = l.stream().filter(x -> x < 0).collect(Collectors.toList());

}

20 of 22

Esempi

//Salvataggio del quadrato dei numeri positivi e pari in una nuova lista

public void test() {

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

List<Integer> res = new ArrayList<>();

for(Integer i : l) {

if(i > 0 && i % 2 == 0)

res.add(i * i);

}

List<Integer> res2 = l.stream()

.filter(x -> x > 0 && x % 2 == 0)

.map(x -> x * x)

.collect(Collectors.toList());

ArrayList<Integer> res3 = l.stream()

.filter(x -> x > 0 && x % 2 == 0)

.map(x -> x * x)

.collect(Collectors.toCollection(ArrayList::new));

}

21 of 22

Esempi

public void test() {

//Somma dei numeri nella lista

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

int sum = 0;

for(Integer i : l) {

sum += i;

}

//Somma dei numeri nella lista con stream

int sum2 = l.stream().mapToInt(x -> x).sum();

//Calcolo della media: se la lista è vuota restituisce un’eccezione

Double d1 = l.stream().mapToInt(x -> x).average().getAsDouble();

//Calcolo della media: se la lista è vuota restituisce NaN

Double d2 = l.stream().mapToInt(x -> x).average().orElse(Double.NaN);

//Calcolo della media: se la lista è vuota siamo noi a restituire un’eccezione

Double d3 = l.stream()

.mapToInt(x -> x)

.average()

.orElseThrow(() -> new IllegalArgumentException("List is empty!"));

}

22 of 22

Esempi

//Creare una lista ordinata

public void test() {

List<Integer> l = List.of(1, 3, 2, -23, 6, 7, -1, -4, 8, 10, 11, 26);

//Ordinamento crescente

List<Integer> sortedList = l.stream().sorted().collect(Collectors.toList());

//Ordinamento decrescente: metodo 1

List<Integer> sortedList = l.stream()

.sorted(Collections.reverseOrder())

.collect(Collectors.toList());

//Ordinamento decrescente: metodo 2

List<Integer> sortedList = l.stream()

.sorted((x1, x2) -> -1 * x1.compareTo(x2))

.collect(Collectors.toList());

//I 5 numeri più piccoli nella lista

List<Integer> s1 = l.stream().sorted().limit(5).collect(Collectors.toList());

//I 5 numeri più piccoli nella lista (senza duplicati)

List<Integer> s2 = l.stream().sorted().distinct().limit(5).collect(Collectors.toList());

}