- Pattern e Matcher e String.split per lavorare su stringhe;
- Formatter e Scanner per lavorare su stream.
Quando si cerca un determinato pattern all'interno di una stringa si possono usare le regular expression; per usarle si crea un Pattern con l'espressione da cercare, e poi si cerca su un testo:
Pattern p = Pattern.compile("o."); // the expression
Matcher m = p.matcher("ciao mondo"); // the source
while(m.find()) {
System.out.println(m.start() + " " + m.end() + " <" + m.group() + ">");
}
stampa
3 5 <o >
6 8 <on>
I metodi Matcher.start() restituisce la posizione attuale nel Matcher (dove è stato trovato il match attuale), Matcher.end() restituisce la posizione finale, e Matcher.group() restituisce l'intero contenuto. Il metodo Matcher.group() permette anche di scegliere il sottogruppo ricercato, nel caso di espressioni regolari più complesse; guardiamo l'esempio seguente:
Pattern p = Pattern.compile("(.)(o)(.)"); // the expression
Matcher m = p.matcher("ciao mondo"); // the source
while(m.find()) {
System.out.println(m.start() + " " + m.end() + " <" + m.group(1) + " - " + m.group(2) + " - " + m.group(3) + ">");
}
restituisce:
2 5 <a - o - >
5 8 <m - o - n>
poiché l'espressione (.)(o)(.) trova tutte le posizioni dove abbiamo una 'o' circondata da due caratteri, e poi divide il risultato in tre gruppi, da cui 'a', 'o', ' ' in "ciao ".
Il passo successivo consiste nell'utilizzare i metacaratteri; disponiamo dei seguenti:
- \d - trova i digits, cioè cifre da 0-9
- \w - trova le word, cioè i caratteri (lettere (tra 'a' e 'z') e (tra 'A' e 'Z') e (tra 0 e 9) o '_')
- \s - trova gli spazi (' ', '\n', '\r', '\t')
oppure possiamo usare le '[' e ']' per racchiudere range di caratteri, per esempio \d è uguale a [0-9] e \w è uguale a [a-zA-Z0-9_]; possiamo usare i quantificatori per indicare quanti elementi vogliamo cercare: se usiamo '+' indichiamo (almeno una), se usiamo '*' indichiamo (tra zero e infinito) e se usiamo '?' indichiamo (o zero o una); se usiamo [^abc] indichiamo (qualsiasi tranne 'a', 'b' o 'c'); se vogliamo indicare un carattere qualsiasi possiamo usare '.'; infine se vogliamo fare ricerche non golose usiamo '*?' al posto di '*' e '?+' al posto di '+'; la differenza tra golose e non è che le golose cercano il match più ampio, mentre le non golose cercano il minore; un esempio è
Pattern greedy: .*xx
Pattern non greedy: .*?xx
Stringa: yyxxxyxx
il match greedy (goloso) da come risultato "yyxxxyxx", mentre il match non greedy da come risultato yyxx e xyxx
Come Matcher, anche Scanner permette di fare ricerche, lavorando su Stream (o su File o su Stringhe):
Scanner s = new Scanner("ciao mondo");
String token;
do {
token = s.findInLine("(.)(o)(.)");
System.out.println("found <" + token + ">");
} while (token != null);
Scanner normalmente viene utilizzato per fare il "tokenizing"; l'alternativa è usare String.split(); vediamo intanto questo e poi come fare con Scanner:
String [] str = "ciao mondo sono molto felice".split(".o.");
for (String s : str)
System.out.print(s + " - ");
che restituisce
ci - - - - o - - felice -
Mentre otteniamo la stessa funzione dello scanner, abbiamo spesso una funzionalità non richiesta: lui esegue il tokenizing di tutta la stringa prima di darci un output; quando questo non è il comportamento desiderato basta utilizzare lo Scanner (che funziona anche su Stream e File, e restituisce tipo primitivi):
Scanner s = new Scanner("ciao 123 cmondo true sdada");
while(s.hasNext()) {
if (s.hasNextInt())
System.out.print("Intero(" + s.nextInt() + "); ");
else if (s.hasNextBoolean())
System.out.print("Boolean(" + s.nextBoolean() + "); ");
else
System.out.print(s.next() + "; ");
}
restituisce:
ciao; Intero(123); cmondo; Boolean(true); sdada;
Abbiamo infine l'oggetto Formatter che ci fornisce i metodi printf e format che si comportano così: