/* *************************************************************************
 * Ora parleremo dell'I/O, ossia Input/Output (Entrata/Uscita).
 * Il sistema I/O del Java ha un'interfaccia indipendente dall'hardware,
 * analoga a quella UNIX del C, che pone un certo livello di astrazione tra
 * il programmatore e il dispositivo in uso, per cui e' analogo accedere a
 * terminali connessi in rete, unita' a disco, a nastro, eccetera.
 * I diversi dispositivi sono considerati un dispositivo logico chiamato
 * FLUSSO (detto stream o pipe). I flussi partono da una sorgente ed arrivano
 * ad una destinazione, astraendosi dai dispositifi fisici che lo permettono.
 * I tipi di flusso in UNIX/ANSI C sono 2:
 *
 * 1) FLUSSO DI TESTO: e' una sequenza di caratteri, che pero' durante il
 *    trasferimento puo' subire anche delle conversioni secondo le necessita'
 *    dell'ambiente di destinazione (ad esempio il carattere di fine riga e'
 *    diverso in certi casi: puo' essere di newline o cr+lf) il che implica
 *    che il flusso eventualmente convertito puo' essere anche di dimensione
 *    diversa. Passando un file non di testo da questo flusso si danneggia!
 *
 * 2) FLUSSO BINARIO: e' una sequenza di byte avente una corrispondenza uno
 *    a uno con la sequenza rivevuta dal dispositivo esterno. Non avvenendo
 *    alcuna conversione il numero di byte scritti/letti rimane uguale.
 *
 * In java sono disponibili anche flussi filtrati piu' sofisticati.
 * Abbiamo gia' visto comandi per I/O da e verso la console (tastiera/video).
 * Ora vedremo il caso di lettura/scrittura di file.
 * In Java le superclassi da cui sono derivati i vari flussi particolari
 * sono InputStream e OutputStream. Le subclassi che ci interessano sono
 * FileInputStream e FileOutputStream, dedicate al flusso di file.
 * Ad esempio:
 *
 *   FileInputStream MioInFilStr = new FileInputStream("Jlez6a.java");
 *   FileOutputStream MioOutFilStr = new FileOutputStream("Jlez6a.bak");
 *
 * Queste classi pero' hanno metodi che possono leggere e scrivere soltanto
 * bytes, e non altri tipi di dati. Se si desiderano leggere/scrivere degli
 * int, dei float, o altri dati, si puo' convertire/filtrare lo stream usando
 * delle classi come DataInputStream:
 *
 *   DataInputStream MioInDatStr = new DataInputStream(MioInFilStr);
 *   DataOutputStream MioOutDatStr = new DataOutputStream(MioOutFilStr);
 *
 * A questo punto saranno disponibili dei metodi piu sofisticati: readChar(),
 * readBoolean(), readFloat(), readLine(); writeChar(), writeFloat(), ecc.
 * Comunque, per maggiore semplicita', leggeremo e scriveremo bytes, usando
 * direttamente InputStream e OutputStream, per creare una copia di questo
 * file sorgente (Jlez6a.java) chiamata Jlez6a.bak.
 * Le eccezioni generate da queste classi sono di tipo IOException:
 *
 *   try {
 *     MioInFilStr = new FileInputStream("Jlez6a.java");
 *   }
 *   catch (IOException e) {
 *     System.out.println("Errore: " + e + " nella lettura di un file");
 *     System.exit(0);
 *   }
 *
 * Attenzione che si scrive su HD, io vi ho avvertito!
 ************************************************************************* */

import java.io.*;                // Include le funzioni I/O standard di base.


class Jlez6a {            // Da qua inizia la classe Jlez6a

 public static void main(String args[])  // Definiamo il metodo Main.
 {                                       // Inizio del main()

// Gli input/output stream e la variabile LungFile li dichiaro nel main(),
// in modo che siano accessibili ovunque. Se fossero dichiarati all'interno
// di un blocco { } di un try o del for, all'esterno di tale blocco non
// esisterebbero. Fate attenzione a questo fatto quando decidete dove
// dichiarare variabili/oggetti! Dichiarateli localmente solo se sono
// effettivamente usati esclusivamente all'interno del blocco. Per esempio
// la variabile i usata nel for e' dichiarata (e usabile) solo nel blocco
// del for, perche' effettivamente non viene usata mai fuori da esso.

    FileInputStream MioInFilStr = null;
    FileOutputStream MioOutFilStr = null;
    int LungFile = 0;

// Provo ad aprire il file Jlez6a.java in lettura con FileInputStream.

    try {
      MioInFilStr = new FileInputStream("Jlez6a.java");
    }
    catch (IOException e) {
      System.out.println("Errore: " + e + " nella lettura di un file");
      System.exit(0);
    }


// Provo ad aprire/creare Jlez6a.bak in scrittura con FileOutputStream.

    try {
      MioOutFilStr = new FileOutputStream("Jlez6a.bak");
    }
    catch (IOException e) {
      System.out.println("Errore: " + e + " nella creazione di un file");
      System.exit(0);
    }

// Se sono arrivato qua senza eccezioni, ho i 2 flussi (stream) attivi.
// Per creare il file di copia bastera' leggere dalla sorgente e scrivere
// nella destinazione tutti i bytes, uno alla volta.

// Per sapere quanti byte posso copiare (cioe' la lunghezza del file) uso
// il metodo int available().

    try {
      LungFile = MioInFilStr.available();   // Mi informo sul num. bytes.
    }
    catch (IOException e) {
      System.out.println("Errore: " + e + " determinando la lungh. di un file");
      System.exit(0);
    }

// Per leggere/scrivere uso FileInputStream.read(), FileOutPutStream.write().
// So quanti bytes devo copiare, grazie ad available().

    try {
      for(int i=0; i