La classe Scanner
permette la lettura di file di dati formattati, utilizzando espressioni regolari per loro localizzazione.
Molti dei suoi metodi richiedono un pattern per indicare come lo scanner debba effettuare il match dell'input. Tali metodi sono dotati di due forme sovraccaricate: una che richiede il pattern sotto forma di un tipo String
e l'altra che lo richiede come un java.util.Pattern
.
Se un input è costituito da un flusso di valori si può interrogare Scanner
per ottenere la restituzione del valore successivo all'interno del flusso. Lo scanner determina tale valore attraverso la decomposizione dell'input in diversi token il cui delimitatore di separazione è rappresentato, di dafault, da uno spazio bianco.
Vediamo un esempio, supponiamo di avere in input un espressione matematica di somma e di volerla processare al fine di calcolarne il risultato. Definiamo preliminarmente uno Scanner per questo input:
private static double sum;
..
public static void main(String[] args) {
String expression = "3.13+2.14+0.16";
Readable source = new StringReader(expression);
Scanner scanner = new Scanner(source);
scanner.useDelimiter("\\+");
}
L'aspetto da evidenziare nel codice precedente, è la definizione del carattere +
come nuovo delimitatore a sovrascrivere quello di default. Possiamo adesso attivare il processo dello scanner attraverso l'utilizzo di una Lambda expression:
scanner.forEachRemaining(elem -> {
sum += Double.parseDouble(elem);
});
System.out.print(expression + "=" + sum);
scanner.close();
Oppure utilizzare un ciclo while:
while(scanner.hasNext()){
sum += Double.parseDouble(scanner.next());
}
System.out.print(expression + "=" + sum);
scanner.close();
Ed ancora, metodi di recupero diretto di un valore numerico se il separatore decimale è rappresentato dalla virgola:
String expression = "3,13+2,14+0,16";
Readable source = new StringReader(expression);
Scanner scanner = new Scanner(source);
scanner.useDelimiter("\\+");
while(scanner.hasNextDouble()){
sum += scanner.nextDouble();
}
System.out.print(expression + "=" + sum);
scanner.close();
Avremo quindi in stampa 3.13+2.14+0.16=5.43 oppure 3,13+2,14+0,16=5.43
Con la classe Scanner
possiamo realizzare operazioni più complesse come ad esempio lo scanning di righe.
Nella modalità a righe, lo scanner può elaborare una riga alla volta utilizzando il pattern di un'espressione regolare. Il matching delle righe viene realizzato attraverso il metodo findInLine(Pattern pattern)
. Tale metodo tenta di individuare il pattern specificato prima che si incorra nel successivo carattere di accapo.
Durante la ricerca i delimitatori vengono ignorati, se il pattern è individuato lo scanner avanza fino alla posizione successiva dopo l'input che ha avuto un match con successo. L'input viene quindi restituito. Se non viene individuato alcun pattern il valore restituito è null
e la posizione dello scanner non avanza rimanendo nella sua posizione.
Analizziamo un altro esempio supponendo di voler elaborare riga per riga i dati ottenuti da un file csv che, per semplicità, collochiamo all'interno di una stringa.
String lines = "Temperatura Max;Temperatura Med;Temperatura Min\n"
+ "13,12;5,00;2,15\n"
+ "24,00;10,12;4,15\n";
Readable source = new StringReader(lines);
Scanner scanner = new Scanner(source);
Pattern pattern = Pattern.compile("^(.*);(.*);(.*)",Pattern.MULTILINE);
while(scanner.hasNextLine()) {
String line = scanner.findInLine(pattern);
Scanner lineScanner = new Scanner(line);
lineScanner.useDelimiter(";");
lineScanner.forEachRemaining(elem -> {
System.out.print(elem);
System.out.print(lineScanner.delimiter());
});
System.out.println();
lineScanner.close();
scanner.nextLine();
}
scanner.close();
Con il primo scanner acquisiamo l'input costituito da più righe, utilizziamo poi su questo scanner la caratteristica multiline delle espressioni regolari per estrarre una riga alla volta attraverso il metodo findInLine()
.
Definiamo quindi un secondo scanner, lineScanner
, per estrarre gli elementi della riga corrente in modo analogo a quanto visto in precedenza. Chiudiamo il lineScanner
ed invochiamo sul primo scanner il metodo nextLine()
per posizionarci sulla riga successiva. L'output sarà del tipo:
Temperatura Max;Temperatura Med;Temperatura Min;
13,12;5,00;2,15;
24,00;10,12;4,15;