Introduzione alle Espressioni Regolari in Java
Introduzione alle espressioni regolari in Java con il pacchetto java.util.regex.
Un'espressione regolare (regex) è un pattern compatto che descrive un insieme di stringhe. In Java, il pacchetto java.util.regex trasforma questi pattern in un piccolo e veloce motore di corrispondenza che puoi applicare a qualsiasi testo — per validare input, cercare sottostringhe, estrarre campi o riscrivere contenuti. Questo capitolo illustra i componenti principali prima che tu inizi a scrivere i tuoi pattern.
Cos'è un'Espressione Regolare
Una regex è semplicemente una stringa scritta in una sintassi speciale, ma Java non la interpreta carattere per carattere ogni volta. Invece si compila il pattern una sola volta in un oggetto Pattern, poi lo si applica all'input tramite un Matcher. La forma compilata è una macchina a stati efficiente, quindi riutilizzare un unico Pattern su molti input è molto più economico che ricompilarlo ogni volta.
I pattern descrivono la struttura: un letterale come cat corrisponde esattamente a quelle lettere, mentre i metacaratteri descrivono forme — \d è qualsiasi cifra, + significa "uno o più", . significa "qualsiasi carattere". Combinandoli puoi descrivere numeri di telefono, email o righe di log in una sola riga di codice.
import java.util.regex.Pattern;
public class FirstPattern {
public static void main(String[] args) {
// Compile the pattern once; reuse the result.
Pattern digits = Pattern.compile("\\d+");
System.out.println(digits.matcher("abc123").find()); // true
System.out.println(digits.matcher("hello").find()); // false
}
}Nota il doppio backslash: \d nella regex deve essere scritto \\d in un letterale stringa Java, perché il compilatore Java consuma prima un backslash.
Pattern e Matcher
Due classi svolgono quasi tutto il lavoro. Pattern è il progetto compilato, riutilizzabile e thread-safe. Matcher è il motore con stato che esegue quel progetto su un input specifico — tiene traccia di dove ci si trova nel testo, quali gruppi hanno catturato e dove è atterrata l'ultima corrispondenza. Crea un nuovo Matcher per ogni input; non condividerne uno tra thread diversi.
| Tipo | Ruolo |
|---|---|
Pattern | Il pattern compilato. Immutabile, thread-safe, riutilizzabile. |
Matcher | Applica un Pattern a un input. Mantiene lo stato delle corrispondenze. |
Pattern.compile(regex) | Costruisce un Pattern da una stringa regex. |
pattern.matcher(input) | Restituisce un Matcher legato a quell'input. |
String.matches(regex) | Helper monouso che compila e verifica la corrispondenza completa in una singola chiamata. |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PatternAndMatcher {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\w+@\\w+\\.\\w+");
Matcher m = p.matcher("ping me at [email protected] please");
if (m.find()) {
System.out.println("Found email: " + m.group());
}
}
}find() vs matches()
L'errore più comune tra i principianti è confondere i due modi di eseguire un pattern. matches() richiede che l'intero input corrisponda al pattern dall'inizio alla fine. find() scansiona per trovare qualsiasi sottostringa corrispondente e può essere chiamato ripetutamente per trovare tutte le occorrenze. lookingAt() si trova a metà strada: è ancorato all'inizio ma non richiede che la corrispondenza arrivi fino alla fine.
| Metodo | Ancorato all'inizio? | Deve corrispondere fino alla fine? | Ripetibile? |
|---|---|---|---|
matches() | sì | sì | no |
lookingAt() | sì | no | no |
find() | no | no | sì |
import java.util.regex.Pattern;
public class FindVsMatches {
public static void main(String[] args) {
Pattern p = Pattern.compile("\\d+");
System.out.println(p.matcher("42").matches()); // true (whole input)
System.out.println(p.matcher("age 42").matches()); // false (extra text)
System.out.println(p.matcher("age 42").find()); // true (substring)
System.out.println(p.matcher("age 42").lookingAt()); // false (no digit at start)
}
}Sintassi Comune su Cui Costruire
La maggior parte dei pattern reali è assemblata da un piccolo vocabolario di blocchi costitutivi: classi di caratteri, scorciatoie predefinite, quantificatori e ancore. Imparare questi elementi ti porta molto avanti. Ogni costrutto ha un capitolo dedicato — vedi Sintassi regex, classi di caratteri e quantificatori per i dettagli completi.
| Costrutto | Significato | Esempio |
|---|---|---|
. | Qualsiasi singolo carattere (eccetto newline) | a.c corrisponde a abc, axc |
\d \w \s | Cifra, carattere parola, spazio | \d\d corrisponde a 42 |
[abc] | Uno qualsiasi tra a, b, c | [aeiou] corrisponde a una vocale |
[^abc] | Qualsiasi carattere tranne a, b, c | [^0-9] corrisponde a un non-cifra |
* + ? | Zero+, uno+, zero-o-uno | ab+ corrisponde a ab, abb |
{n} {n,m} | Esattamente n, tra n e m | \d{3} corrisponde a 555 |
^ $ | Inizio, fine dell'input/riga | ^Hi corrisponde a un Hi iniziale |
(...) | Gruppo di cattura | (\d{4}) cattura quattro cifre |
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GroupsExample {
public static void main(String[] args) {
// Two capturing groups: year and month.
Pattern date = Pattern.compile("(\\d{4})-(\\d{2})");
Matcher m = date.matcher("Released 2025-11 to users");
if (m.find()) {
System.out.println("Full: " + m.group(0)); // 2025-11
System.out.println("Year: " + m.group(1)); // 2025
System.out.println("Month: " + m.group(2)); // 11
}
}
}Esempio Pratico
Il programma seguente compila un pattern per numeri di telefono e mette alla prova l'intera API: percorre ogni corrispondenza con find(), legge i gruppi di cattura e le posizioni delle corrispondenze, contrasta find() con matches() e riscrive il testo con replaceAll(). Eseguilo per osservare il motore in azione.
Cosa osservare dall'esecuzione:
find()viene chiamato in un ciclo e restituisce due corrispondenze, quindi lo stessoMatcherpercorre il testo un'occorrenza alla volta finché non restituiscefalse.group(1)egroup(2)restituiscono le sotto-parti tra parentesi (555e1234), mentregroup()senza argomenti restituisce l'intera corrispondenza.start()eend()riportano gli offset dei caratteri di ogni corrispondenza, utile per evidenziare o ritagliare il testo originale.matches()sull'intera frase stampafalseperché il pattern non copre l'intera stringa, mentre"555-1234"da solo stampatrue— prova chematches()funziona solo sull'intero input.replaceAll("XXX-XXXX")riscrive ogni corrispondenza in un solo passaggio, producendo la frase mascherata e mostrando come i pattern guidano la trasformazione del testo.
Dove Andare Ora
Ora che conosci i componenti principali — Pattern, Matcher e la differenza tra find() e matches() — approfondisci con i capitoli tematici:
- Pattern e Matcher — l'API completa per compilare ed eseguire pattern.
- Sintassi regex — ogni metacarattere e sequenza di escape, spiegati.
- Classi di caratteri — insiemi, intervalli e negazione.
- Quantificatori — ripetizione greedy, riluttante e possessiva.
- Gruppi di cattura — estrazione e backreference delle sotto-parti.
- Flag — case-insensitive, multiriga e altri modificatori.