|
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ó: |
|
|
 |
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 |
|
|
 |
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.
|
|
|
 |
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.
|
|
|
 |
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.
|
|
|
 |
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 |
|
|
 |
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ó). |
|
|
|
|
|
|
 |
|
|
|
|