archiviareTesto

leggere e scrivere file di testo

L'esigenza è quella di memorizzare (e leggere) le informazioni nel formato testo (e con formato testo non intendiamo "doc" o "odt" ma un formato "testo piano").

Leggere un testo

La prima cosa che voglio fare è leggere il mio file (si chiama gruppo.txt e sta sul mio desktop) che contiene un elenco di persone che voglio visualizzare sullo schermo.

Come spesso succede la stessa cosa si può fare in più modi, per adesso facciamo così:

creiamo un oggetto che è in grado di leggere flussi di caratteri (un FileReader)
creiamo un oggetto che è in grado di leggere una riga di testo alla volta (un BufferedReader)
leggeremo una riga alla volta fino ad arrivare fino alla fine del file
    ogni elemento letto lo accodiamo nella variabile gruppo
visualizziamo il contenuto di gruppo nell'interfaccia

Quello che segue è la parte di programma che legge le informazioni, l'interfaccia è lasciata per esercizio.

FileReader lettore;
BufferedReader lettoreDiRighe;
String testo;
String gruppo = "";
try{
   lettore = new FileReader("/Users/Edoardo/Desktop/gruppo.txt");
   lettoreDiRighe = new BufferedReader(lettore);
   while( (testo=lettoreDiRighe.readLine()) != null){
      gruppo = gruppo + " " + testo;
   }
   lettoreDiRighe.close();
   lettore.close();
   casellaGruppo.setText(gruppo);
}catch(Exception ex){
   casellaerrori.setText(ex.getMessage());
}

Gli oggetti FileReader e BufferedReader sono contenuti nel pacchetto java.io così come gli altri oggetti nuovi introdotti nel resto capitolo.

Al posto del ciclo while nel programma sopra scrivo questo:

for(testo=lettoreDiRighe.readLine(); testo!=null; testo=lettoreDiRighe.readLine()){
  gruppo = gruppo + " " + testo;
}
nodevi rileggere il testo con più attenzione... o forse il ciclo for si

Come esercizio scrivi il programma qui sopra utilizzando try-with-resources

Testi separati da virgole

Sono dei file molto comuni chiamati usualmente csv (Comma Separated Values) che possiamo ottenere amche salvando i dati presenti in un foglio di calcolo.

Questi file rappresentano una tabella in cui i dati di ogni colonna sono separati da una virgola e in cui tutte le righe hanno lo stesso numero di colonne (NB: una riga vuota rappresenterebbe una ulteriore riga di dati ma con zero colonne).

Prendiamo come esempio un file in cui registriamo le nostre spese: ogni riga contiene in sequenza una data, descrizione, denaro entrato, denaro uscito. Ad esempio le prime tre righe sono:

10-8-2017,pizzeria,0,25
11-8-2017,regalo,50,0
10-8-2017,pizzeria,0,35

Dovremmo ora costruire un programma che leggendo una riga alla volta scriva poi il totale delle entrate e delle uscite.

public class CommaSeparatedValues extends Application{
  TextField cIngressi = new TextField();
  TextField cUscite = new TextField();
  Label eErrori = new Label("-");

  @Override
  public void start(Stage primaryStage) throws Exception {
    Label eIngressi = new Label("ingressi:");
    Label eUscite = new Label("uscite:");
    GridPane pannello = new GridPane();
    Button contabilizza = new Button("contabilizza");
        
    pannello.add(eIngressi, 0, 0);
    pannello.add(cIngressi, 1, 0);
    pannello.add(eUscite, 0, 1);
    pannello.add(cUscite, 1, 1);
    pannello.add(contabilizza, 0, 3);
    pannello.add(eErrori, 1, 3);
        
    Scene scena = new Scene(pannello);
    primaryStage.setScene(scena);
    primaryStage.setTitle("contabilità");
    primaryStage.show();
        
    contabilizza.setOnAction(e->calcolaSomme());
  }
    
  public void calcolaSomme() {
    String riga;
    String parti[];
    int ingresso = 0;
    int uscita = 0;
    try(
      FileInputStream fis = new FileInputStream("/Users/edoardo/Desktop/spese.txt");
      InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
      BufferedReader input = new BufferedReader(isr);
    ){
      while( (riga=input.readLine()) != null ) {
        parti = riga.split(",");
        ingresso += Integer.parseInt(parti[2]);
        uscita += Integer.parseInt(parti[3]);
      }
      cIngressi.setText(""+ingresso);
      cUscite.setText(""+uscita);
    } catch (NumberFormatException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
     
  public static void main(String args[]){
    launch();
  }
}

In questo caso nella creazione dell'InputStreamReader abbiamo specificato anche la codfifica dei caratteri.

Scrivere testi

Il lavoro da fare questa volta è salvare informazioni in un file di testo: il programma qui sotto legge le informazioni da una casella di testo e le scrive in un file.

public class ScrivereTesti extends Application{
  TextField cTesto = new TextField();
  Label eErrori = new Label("-");

  @Override
    public void start(Stage primaryStage) throws Exception {
    Label eIngressi = new Label("testo:");
    GridPane pannello = new GridPane();
    Button salva = new Button("accoda al file");
        
    pannello.add(eIngressi, 0, 0);
    pannello.add(cTesto, 1, 0);
    pannello.add(salva, 0, 1);
    pannello.add(eErrori, 1, 1);
        
    Scene scena = new Scene(pannello);
    primaryStage.setScene(scena);
    primaryStage.setTitle("parole");
    primaryStage.show();
        
    salva.setOnAction(e->calcolaSomme());
  }
    
  public static void main(String args[]){
    launch();
  }
    
  public void calcolaSomme() {
    String riga;
    try(
      FileOutputStream fos = new FileOutputStream("/Users/edoardo/Desktop/testi.txt",true);
      OutputStreamWriter uscita = new OutputStreamWriter(fos,"UTF-8");
    ){
      riga = cTesto.getText();
      uscita.write(riga);
      uscita.write("\n");
    } catch (Exception e) {
      eErrori.setText(e.getMessage());
    }
  }
}

Nel programma qui sopra ho aperto un FileOutputStream ma non ho usato close() dopo aver letto... perché?

dimenticanza no, fatto appositamente ho usato un try-with-resources si! visibile dalle tonde dopo la parola chiave try non è necessario no, potrei perdere dati che volevo salvare

Il meccanismo non è molto diverso da quello usato per la lettura dei dati, vediamo però nel dettaglio cosa fanno le righe evidenziate.

FileOutputStream fos = new FileOutputStream("/Users/edoardo/Desktop/testi.txt",true)
FileOutputStream è un oggetto che ci permette di scrivere sequenze di bytes su disco (funzione speculare rispetto a FileInputStream) e può essere creato analogamente utilizzando un solo parametro (il nome del file) ma in questa occasione abbiamo deciso di utilizzare il costruttore con due argomenti: il secondo (quello booleano) se è impostato a true indice al FileOutputStream che non deve iniziare a scrivere dall'inizio del file ma deve aggiungere in fondo le nuove informazioni.

OutputStreamWriter uscita = new OutputStreamWriter(fos,"UTF-8")
L'oggetto OutputStreamWriter serve per scrivere sequenze di caratteri, anche in questo caso abbiamo usato il costruttore con due parametri (ne esiste anche uno con il solo FileOutputStream come parametro), il secondo è la codifica da utilizzare nello scrivere il file: se non è specificata java usa quella di default del sistema ma... non tutti i sistemi hanno la stessa codifica e quindi è possibile che alcuni simboli (ad esempio le lettere accentate) vengano lette in maniera erronea se trasferisco il file scritto su un computer con un diverso sistema operativo. La codifica scelta è UTF-8 perché permette di salvare tutti i caratteri Unicode.

All'interno del programma trovo un uscita.write("\n");: cosa significa quel "\n"

barra e poi n no, la barra rovescita (backslash) serve per indicare caratteri speciali errore nel programma "a capo"