Enrera
Mòdul 3
Iniciació a la programació en Java
  Pràctica
1
2
3
4
   
Exercicis
Exercicis
 
 
Entrada i Sortida
   

Els programes d'ordinador no viuen pas aïllats: recullen informació de bases de dades, de fitxers, del teclat, d'ordinadors remots, etc. També cedeixen informació a d'altres programes, a dispositius com ara la pantalla o a fitxers enregistrats al disc dur...

Tots aquest processos són coneguts com a operacions d' entrada i sortida. Java gestiona aquestes operacions a través d'uns objectes específics, els objectes de flux (streams).

Podem entendre els objectes de flux com el canal o pont que Java construeix entre l'origen o destí de les dades i el nostre programa. A través d'aquests canals poden fluir tot tipus de coses (so, text, nombres) tot i que això no és gaire rellevant: l'arquitectura del canal no és pas problema del programador perquè Java ja s'encarrega d'aquest aspecte. Nosaltres només hem de pensar en com gestionem el flux de dades que cau de la font, i també en com obrir i tancar adequadament l'aixeta.

Des del començament del curs hem fet servir objectes de flux sense ser-ne gaire conscients: hem estat treballant amb System.out, un camp (field) predeclarat la classe base del qual és java.io.PrintStream, un objecte stream que dóna funcionalitats d' impressió.

De fet, System és una classe que ens proporciona de manera automàtica tres canals:

System.in stream de recepció de dades des dels dispositius estàndard d'entrada (stdin).
System.out stream d' escriptura als dispositius estàndards de sortida (stdout).
System.err canal per a la presentació d' errors que, habitualment, serà la pantalla.

Pel que fa a les sortides, com que System.out i System.err són objectes PrintStream, disposen de tres mètodes molt senzills de visualització d' informació:

  • print()

  • println()

  • write().

Els mètodes print() i println() fan el mateix: imprimeixen objectes Object, String, int, double, etc. La diferència rau en què println() afegeix un salt de línia ('\n') al final de l'objecte de flux. El mètode write() es fa servir per escriure bytes a l'stream, és a dir, dades que no poden ser interpretades com a text, com per exemple les dades que componen un gràfic.

Així doncs,cada vegada que en un programa hagi escrit una cosa com aquesta:

System.out.println("aquest text viatjarà cap al dispositiu de
                   sortida i s' imprimirà");

haurem estat utilitzant el camp out de la classe java.lang.System (la qual no cal instanciar).Aquest camp és un objecte stream, (fill de la classe java.io.PrintStream), pensat per a la sortida i impressió de dades. D'aquesta classe n'hem utilitzat el mètode println() que recull les nostres dades, les transforma en una cadena (string), i les imprimeix en el dispositiu de sortida estàndard: la pantalla.

Llàstima que el camp System.in no sigui simètric a System.out!. Per tractar les entrades, haurem de treballar una mica més!.

   
  Entrada de caràcters des del teclat:
   
 

System.in també és un camp predeclarat, però a diferència de System.out, la seva classe base és java.io.InputStream, una classe abstracta. Les classes abstractes no es poden utilitzar directament: el programador no pot crear una instància d'java.io.InputStream, sinó que ha de fer-ne anar alguna classe filla.Però no hem de patir massa perquè tenim classes filles disponibles: podem utilitzar, per exemple, la classe java.io.InputStreamReader.

Per tant, per a llegir una entrada hem de fer:

   
 

import java.io.*;
InputStreamReader caudal_entrada = new InputStreamReader(System.in);

   
 

i ja disposem del mètode read() de l'objecte InputStreamReader per tal d'accedir a la informació d'entrada.

Si, com és molt freqüent, hem de fer lectures des del teclat, el SDK de Java ens dóna una altra classe més pràctica, l'objecte BufferedReader, que té la funció de col·locar en un buffer tot el que escrivim amb el teclat fins que premem la tecla Retorn. Hem de complicar una mica més la declaració de l'stream, però valdrà la pena:

   
 

import java.io.*;
BufferedReader cabal_entrada = new BufferedReader
                               (new InputStreamReader(System.in));

   
 

Ara sí. Aquest objecte, cabal_entrada, que acabem de crear ja és completament funcional. Accedirem a la informació que ens vagi arribant a través del mètode readLine(), el qual es comporta d'una forma similar al mètode println() ja conegut.

Hem de tenir en compte, però, que l'entrada de dades ens pot fallar en qualsevol moment. Li hem de dir a Java que es pot produir un error d'entrada/sortida al nostre mètode i que s'encarregui d'interceptar-lo si és que es produeix. Per això, qualsevol mètode que inclogui un BufferedReader (o qualsevol altre objecte de flux d'entrada ) s'ha de protegir amb la clàusula un throws IOException a la declaració del mètode. En els següents exemples veurem com es fa això.

   
Identificar-se per a entrar a una aplicació:
   
Pràctica Suposem que volem establir un sistema d'identificació d'usuaris; els demanarem el seu nom i la seva contrassenya abans d'entrar al nostre programa:
   
 

import java.io.*;

/**
* Programa per a la identificació d'usuaris
* -------------------------------------------------------------
*/

public class Identificacio {

    public static void main(String[] args) throws IOException {
        BufferedReader entrada = new BufferedReader(
                                 new InputStreamReader(System.in));
        System.out.println("Quin és el seu nom?");
        String elnom = entrada.readLine();
        System.out.println("Quina és la seva contrassenya?");
        String lacontrassenya = entrada.readLine();
        System.out.println("Benvingut " + elnom +
                           " al sistema. Pot procedir");
    }

}

   
Sumant l'IVA a un preu entrat pel teclat
   
Pràctica Ens demanen un programa que, donat un preu, ens calculi l'import de l'IVA i el sumi al valor inicial.
   
 
import java.io.*;

/**
* Programa per a fer el càcul de l' IVA
* ---------------------------------------------------------------
*/

public class SumaIVA {

    public static void main(String[] args) throws IOException {
        BufferedReader entrada = new BufferedReader(new
                                     InputStreamReader(System.in));
        System.out.println("Import sense IVA ");
        String cadena = entrada.readLine();
            try {
                double limport = Double.parseDouble(cadena);
                System.out.println("Tipus d'IVA aplicat");
                cadena = entrada.readLine();
                int tipusiva = Integer.parseInt(cadena);
                double importiva = (limport*tipusiva)/100;
                System.out.println("Import de l'IVA: "+importiva);
                limport+=importiva;
                System.out.println("Import del producte: "+limport);
            } catch(Exception e) {
                System.out.println("Heu d'introduir un número per a
                                   l'import i el tipus d'IVA.");
        }
    }

}

   
 

Podem observar que el programa està força protegit: captarà qualsevol error d'entrada/sortida mitjançant la clàusula throws IOException i també els errors de l'usuari quan aquest no entri un nombre per a l'import a calcular o pel tipus d'IVA. Estudiarem amb més profunditat les tècniques de protecció davant dels errors a la pràctica següent.

   
  Gestió del flux de dades a fitxers i des de fitxers (I). Lectura seqüencial:
 
La lectura-escriptura seqüencial dels continguts de fitxers la farem a través de les classes java.io.FileInputStream i java.io.FileOutputStream.
   
 

La classe java.io.FileInputStream és una classe no abstracta que és filla de java.io.InputStream. Aquesta classe llegeix dades des dels fitxers de manera seqüencial. El sistema operatiu o el sistema de fitxers sobre el qual està muntat el fitxer no té cap importància i, per tant, procedirem de la mateixa manera amb Windows, Linux o Mac. La manipulació de la informació procedent del fitxer l'haurà de fer el programa Java. La classe java.io.FileInputStream fa exclusivament la lectura byte a byte del contingut del fitxer.

   
Pràctica Ara escriurem un petit programa, el qual obrirà el fitxer que li indiquem des del teclat, el llegirà fins al final i ens farà un informe sobre el nombre de vocals que hagi pogut comptar: obrim JCreator, creem el fitxer ComptadorVocals.java, el compilem i l'executem. Per tal de facilitar les coses, és convenient deixar el fitxer que vulguem analitzar al mateix directori on hi hagi ComptadorVocals.class.
   
import java.io.*;

/**
* Programa per a comptar les vocals d'un fitxer
* ----------------------------------------------------------------
*/


class ComptadorVocals {

    public static void main(String args[]) throws
                              FileNotFoundException, IOException {
        // Comptador per a cada tipus de vocal
        int as=0, es=0, is=0, os=0, us=0;
        int n=0;
        int tots=0;

        // -------- Entrem el nom del fitxer a analitzar ---------
        BufferedReader entrada = new BufferedReader(
                                new InputStreamReader(System.in));
        System.out.println("Nom del fitxer?");
        String nomfitxer = entrada.readLine();

        // -------- Creem el Stream de fitxer i el llegim --------
        FileInputStream fitxer = new FileInputStream(nomfitxer);
            try {
                    while (n!=-1) { // n és igual a -1 al final del
                                    // fitxer

                        tots++;
                        n=fitxer.read();
                            switch( n ) {
                                case 97 : as+=1; break;
                                case 101 : es+=1; break;
                                case 105 : is+=1; break;
                                case 111 : os+=1; break;
                                case 117 : us+=1; break;
                            }
                }

            // ---------- Fem l'informe --------------------------
            System.out.println("Resum de vocals:");
            System.out.println("----------------");
            System.out.println("El fitxer conté:");
            System.out.println("'a': "+as+" vegades");
            System.out.println("'e': "+es+" vegades");
            System.out.println("'i': "+is+" vegades");
            System.out.println("'o': "+os+" vegades");
            System.out.println("'u': "+us+" vegades");
            System.out.println("Nº de vocals: "+(as+es+is+os+us));
            System.out.println("% vocals sobre els caracters:"+
                               ((as+es+is+os+us)*100/tots)+"%");
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            fitxer.close();
        }
    }

}

   
  Gestió del flux de dades a fitxers i des de fitxers (II). Escriptura seqüencial:
   
  El procés contrari, l'enregistrament de fitxers, és simètric al de lectura. El porta a terme la classe java.io.FileOutputStream.
   
Pràctica A continuació escriurem un programa que enregistra deu nombres enters al fitxer enters.dat. Com que l'escriptura també es fa byte a byte, passarem la informació per un filtre abans d'enregistrar-la físicament. D'aquesta forma el fitxer contindrà nombres que es poden llegir a simple vista i no el seguit de caracters ininteligibles que causarien una sortida en brut des de la classe java.io.FileOutputStream.. El filtre és de la classe java.io.DataOutputStream i és capaç de transformar informació als tipus primitius int, float, double, etc.
   
 
import java.io.*;

/**
* Programa per a enregistrar al disc deu nombres enters
* -----------------------------------------------------------------
*

public class EnregistraEnters {

    public static void main(String[] args) throws IOException {
        FileOutputStream fitxer = new FileOutputStream(
                                                     "enters.dat");
        DataOutputStream filtre = new DataOutputStream(fitxer);
            try {
                    for (int n=0; n<10; n++) {
                        filtre.writeBytes(n+"\n");
                    }
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }finally {
                fitxer.close();
            }
    }
}

   
  Lectura dels atributs d'un fitxer. Les classes File i FileDescriptor
   
 

A vegades d'un fitxer ens interessa saber-ne el camí (path) absolut, quina quantitat d'espai de disc ocupa, si és només de lectura, o potser volem crear un fitxer o director, i/o saber els fitxers que hi ha en una carpeta, etc.

Aquestes operacions, que no són d'entrada-sortida, Java les gestiona a través de les classes java.io.File i java.io.FileDescriptor. Com tot a Java, una bona característica d'aquestes classes és que treballen igual de bé a qualsevol plataforma i el programador no necessita conéixer les peculiaritats del sistema de fitxers de cada sistema operatiu.

   
Pràctica Ara escriurem un programa que ens llistarà els fitxers del directori actual, una mica a l'estil Linux...
   
 
import java.io.*;
import java.util.*;

/**
* Programa per a fer la llista del contingut del directori actual
* -----------------------------------------------------------------

public class Directori {

    public static void unfitxer(File f) {
        if (f.exists()) {
            System.out.print(f.canRead() ? "r":"-");
            System.out.print(f.canWrite() ? "w":"-");
            System.out.print(f.isDirectory() ? "d":"-");
            System.out.print(f.isHidden() ? "h" : "-");
            System.out.print("\t");
            System.out.print(f.length());
            System.out.print("\t");
            System.out.print(new Date(f.lastModified()));
            System.out.print(" "+f.getName()+"\t");
            System.out.print("\n");
        } else {
            System.out.print("\n");
        }
    }

    public static void main(String[] args) {
        // Directoris i fitxers utilitzen la mateixa classe File

        // "." és el directori actual
        File directori = new File(".");
        String[] elsfitxers = directori.list();
        for (int n=0; n<elsfitxers.length; n++) {
            unfitxer(new File(elsfitxers[n]));
        }
     }

}

   
Lectura de fitxers *.properties
   
Pràctica

Molts programes guarden elements de configuració en fitxers de text simple (plain text) que el programa llegeix quan es posa en marxa. A l'entorn Windows són freqüents els fitxers *.ini. El SDK de Java disposa dels fitxers *.properties. Quan necessitem que el valor d'una constant pugui ser modificada fora del codi del programa podem posar-la en un fitxer *.properties.

Creeu un fitxer de text, amb el nom configuracio.properties, que tingui el següent contingut:

   
  usuari=Josep Capdevila Marc
localitat=Lleida
   
  i, al mateix directori on haureu deixat aquest fitxer, creeu i compileu el programa de nom Propietats.java:
   
 
import java.util.Properties;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;

/**
* Lectura de fitxers de propietats
* -----------------------------------------------------------
*/

public class Propietats {

    static String usuari="";
    static String localitat="";

    static void getPropietats()
                        throws FileNotFoundException, IOException {
        Properties props=new Properties();
        FileInputStream in = new FileInputStream(
                                        "configuracio.properties");
        props.load(in);
        usuari=props.getProperty("usuari");
        localitat=props.getProperty("localitat");
    }


    public static void main(String args[]) {
            try {
                getPropietats();
            } catch(Exception e) {
                System.out.println(e.getMessage());
            }
        System.out.println("Benvingut sr/a: "+usuari);
        System.out.println("Localitat: "+localitat);
    }
}

   
  Per a aquest programa utilitzem la classe java.util.Properties que gestiona atributs segons el parell "nom de l'atribut = valor de l'atribut". Pot recollir tots els atributs des d'un fitxer amb el mètode load(FileInputStream) i retornar el valor de cadascun d'ells amb el mètode getProperty( <nom de l'atribut> ) (vegeu-ne la documentació).
   
   
 
Amunt