Fluxuri de intrare/iesire
Clasele ce ofera facilitati de intrare/iesire se gasesc în pachetul java.io
Una dintre metodele prin care putem îmbogati functionalitatea unei clase C este cea de a declara în ea un câmp d de tipul altei clase D si de a initializa acest câmp printr-un constructor:
class C
. . .
În acest mod, în metodele clasei C putem invoca metode ale clasei D, prin intermediul obiectului d.
În cazul în care clasa D extinde clasa C, aceasta tehnica poarta numele de suprapunerea de obiecte, care aplicata la fluxuri de intrare/iesire se numeste suprapunere de fluxuri. În aceasta situatie sintagma "suprapunerea obiectelor adauga noi facilitati (functionalitati)", înseamna adaptarea metodelor clasei suprapuse la contextul clasei care o suprapune.
Facilitatile de intrare/iesire din Java au la baza notiunea de flux. Un flux este o succesiune de elemente (octeti sau caractere), citite si scrise secvential.
Pentru un flux de intrare sursa datelor poate fi un fisier, dar si un sir sau tablou de octeti, respectiv caractere. Pentru un flux de iesire, datele transmise sunt stocate într-un fisier sau într-un tablou de octeti, respectiv caractere. Este posibila (si chiar recomandata) utilizarea zonelor tampon. De asemenea este posibil ca un flux de iesire sa "comunice" cu un flux de intrare, în sensul ca datele scrise în fluxul de iesire vor constitui sursa pentru fluxul de intrare.
Daca la citire nu sunt înca date disponibile în flux si nu s-a detectat sfârsitul fluxului, atunci firul de executare care realizeaza citirea va fi blocat pâna când vor exista date disponibile. Analog, în cazul a doua fluxuri comunicante ce folosesc o zona tampon, firul de executare care are sarcina sa scrie va fi blocat în situatia în care zona tampon este plina.
O prima clasificare a fluxurilor are în vedere elementele de baza care sunt transmise: caractere sau octeti.
Object InputStream (clasa abstracta pt.
citire la nivel de octet) OutputStream (clasa abstracta pt. scriere
la nivel de octet) Reader (clasa abstracta pt. citire la nivel
de caracter) Writer (clasa abstracta pt. scriere la nivel
de caracter)
Toate clasele, interfetele si metodele din pachetul java.io au modificatorul public, iar în plus aproape toate metodele contin clauza throws IOException
Fluxuri ce lucreaza la nivel de octet
O structura simplificata de clase
Object DataInput (interfata) InputStream (abstracta) FileInputStream FilterInputStream DataInputStream implements DataInput DataOutput (interfata) OuputStream (abstracta) FileOutputStream FilterOutputStream DataOutputStream implements DataOutput
Clasele neabstracte, afara de FileInputStream si de FileOutputStream, au un constructor cu un parametru de tipul InputStream, respectiv de tipul OutputStream
Exemplul 1. Într-o prima etapa vom citi de la intrarea standard un numar natural n si apoi n numere reale; vom crea în directorul curent un fisier cu numele out.dat în care vom scrie datele citite. Într-o a doua etapa vom citi din fisierul out.dat valoarea n si cele n numere si le vom tipari la iesirea standard.
Prima etapa este realizata de urmatorul program:
import java.io.*;
class Unu
unde:
- prin new FileOutputStream("out.dat") este creat fisierul out.dat
- obiectul g de tipul DataOutputStream foloseste metodele writeInt si writeDouble (anuntate în interfata DataOutput si implementate în clasa DataOutputStream) pentru a scrie în fisierul out.dat
- pentru închiderea fisierului este invocata metoda close a clasei FilterInputStream (mostenita din DataOutputStream
A doua etapa este realizata de urmatorul program:
import java.io.*;
class Doi
unde:
- prin new FileInputStream("out.dat") este deschis fisierul out.dat
- obiectul g de tipul DataInputStream foloseste metodele readInt si readDouble (anuntate în interfata DataInput si implementate în clasa DataInputStream) pentru a citi din fisierul out.dat
- pentru închiderea fisierului este invocata metoda close a clasei FilterInputStream (mostenita din DataInputStream
. Interfata DataOutput
Contine metode menite a scrie, într-un flux de iesire neprecizat, date de tipuri primitive, precum si siruri de caractere.
void write(int b)
void writeBytes(String s) throws NullPointerException
void writeChars(String s) throws NullPointerException
void writeUTF(String s) void writeBoolean(boolean v)
void writeByte(int v) void writeShort(int v)
void writeChar(int v) void writeInt(int v)
void writeLong(long v) void writeFloat(float v)
void writeDouble(double v)
. Interfata DataInput
int skipBytes(int n) boolean readBoolean()
byte readByte() int readUnsignedByte()
short readShort() int readUnsignedShort()
char readChar() int readInt()
long readLong() float readFloat()
double readDouble() String readLine()
String readUTF()
Observatie. Daca s-a ajuns la sfârsitul fluxului de intrare înainte de a se fi citit numarul dorit de octeti, va fi lansata exceptia EOFException
Se presupune ca datele citite au fost scrise în fisier cu metodele complementare anuntate în interfata DataOutput
. Clasa abstracta OutputStream
Adauga:
void flush() void close()
. Clasa abstracta InputStream
Adauga:
long skip(long n) int available() void close()
class FilterOutputStream extends OutputStream
class FilterInputStream extends InputStream
. Clasele FileOutputStream si FileInputStream
Metodele clasei FileOutputStream implementeaza, respectiv redefinesc metodele cu aceleasi nume si signatura din OutputStream, aplicându-le pentru fluxul de iesire primit ca argument de constructor.
class FileOutputStream extends OutputStream
Constructorul cu un parametru deschide fluxul de iesire nume
class FileInputStream extends InputStream
Constructorul clasei deschide fluxul de intrare constituit de fisierul precizat prin sirul de caractere nume
. Clasele DataOutputStream si DataInputStream
Clasele DataOutputStream si DataInputStream ofera în plus posibilitatea ca fluxurile sa nu mai fie privite la nivel de octet, ci ca succesiunide date primitive sau siruri de caractere. Datele vor fi scrise în fluxul de iesire într-un format independent de modul de reprezentare al datelor în sistemul pe care se lucreaza.
class DataOutputStream extends FilterOutputStream
implements DataOutput
class DataInputStream extends FilterInputStream
implements DataInput
O structura extinsa de clase
Object DataInput (interfata) InputStream (clasa
abstracta) FileInputStream FilterInputStream DataInputStream BufferedInputStream PipedInputStream DataOutput (interfata) OutputStream (clasa
abstracta) FileOutputStream FilterOutputStream DataOutputStream Buffered OutputStream PrintStream PipedOutputStream
Exemplul 2. Adaptam Exemplul 1 la lucrul cu zone tampon, utilizând un buffer pentru fluxul de intrare din clasa Doi. Pentru aceasta este suficient sa înlocuim declararea fluxului g prin:
DataInputStream g = new DataInputStream(
new BufferedInputStream(
new FileInputStream("out.dat") ) );
. Clasele PipedOutputStream si PipedInputStream
Functionalitatea noua oferita de aceste clase consta în crearea a câte un obiect pos, respectiv pis de fiecare din aceste tipuri, obiecte ce sunt "conectate" în urmatorul sens: ceea ce este scris prin intermediul obiectului pos este citit prin intermediul obiectului pis. Fiecare dintre obiecte (fluxuri) cunoaste identitatea celuilalt. Este recomandat sa folosim fire de executare separate pentru utilizarea celor doua obiecte, deoarece încercarea de a realiza acest tip de transmisie din cadrul aceluiasi fir de executare poate conduce la blocare totala. Este folosita o zona tampon cu disciplina de coada. Orice obiect de unul dintre tipurile PipedInputStream si PipedOutputStream trebuie conectat la exact un obiect de celalalt tip, în caz contrar fiind lansata o exceptie.
PipedInputStream extends InputStream
Constructorul cu un parametru creeaza un obiect de tipul PipedInputStream si îl conecteaza la obiectul pos primit ca parametru. Constructorul fara parametri creaza un obiect, dar nu realizeaza conectarea; aceasta trebuie realizata ulterior prin invocarea metodei connect
Câmpul PYPE_SIZE reprezinta marimea cozii de intrare, ale carei elemente apar în buffer. Câmpurile in si out indica pozitiile în care va fi primit urmatorul octet, respectiv pozitia din care va fi citit primul octet din acest flux de intrare. Coada vida este identificata prin in<0, iar coada plina este identificata prin in==out
Prin invocarea metodei receive este primit un octet de intrare.
class PipedOutputStream extends InputStream
Constructorul cu un parametru creeaza un obiect de tipul PipedOutputStream si îl conecteaza la obiectul pis primit ca parametru. Constructorul fara parametri creeaza un obiect, dar nu realizeaza conectarea; aceasta trebuie realizata ulterior prin invocarea metodei connect
Observatie. Daca fluxul de intrare pis si fluxul de iesire pos sunt ambele neconectate, conectarea lor se poate realiza si prin oricare dintre invocarile pis.connect(pos) si pos.connect(pis)
Exemplul 4. Reuam problema Producator - Consumator; sunt transmise valorile din intervalul
class PIS extends PipedInputStream
int OUT()
class Banda
catch (IOException e)
}
synchronized void pune(int b)
IO.write(" P" + b); pos.write(b); notify();
}
catch (IOException e)
catch(InterruptedException e)
}
synchronized int ia()
b = pis.read(); IO.write(" C" + b); notify();
}
catch (IOException e)
catch(InterruptedException e)
return b;
}
class Prod extends Thread
public void run()
catch(InterruptedException e) ;
}
class Cons extends Thread
public void run()
catch(InterruptedException e)
}
while (b<99);
}
class Piped
Clasa Scanner
Clasa Scanner apare în pachetul java.util si permite regasirea într-un text a tipurilor primitive si a sirurilor, folosind expresii regulate.
Un scanner (obiect de tipul Scanner) împarte intrarea în entitati, folosind un sablon de delimitatori (delimitatorii impliciti sunt spatiile albe) si regaseste entitatile prin invocarea de metode next. Pentru a verifica daca urmeaza o entitate de un anumit tip se folosesc metodele hasNext.
Exemplu. Citirea unui întreg de la intrarea standard se poate realiza astfel:
Scanner sc = new Scanner(System.in);Intrarea poate fi si un fisier text:
Scanner sc = new Scanner(new File("fisier"));sau un sir de caractere.
Exemplu. Programul urmator realizeaza citirea repetata de la intrarea standard si introducerea într-un fisier "text" a unei succesiuni de întregi terminata cu o entitate diferita de un întreg, pâna este detectat sirul "STOP".
import java.util.*; import java.io.*;
class Scan
while(!s.equals("STOP"));
out.close();
}
unde despre clasa PrintWriter mentionam doar ca actioneaza la nivel de caracter si pune la dispozitie metodele print si println fara argumente sau cu un argument ce poate fi un tip primitiv sau un sir de caractere.
Metodele de scanare relative la citire pot conduce la blocare prin asteptarea unei valori de intrare.
Metodele
next()
si hasNext()
,
precum si metodele asociate tipurilor primitive (ca de exemplu nextInt()
si hasNextInt()
),
încep prin a "sari" peste intrarile ce corespund
sabloanelor de intrare si apoi încearca sa
regaseascaurmatoarea entitate.
Un
obiect de tipul Scanner
nu este adecvat lucrului cu fire de executatre decât în prezenta
sincronizarii.
Constructorul
are un parametru de unul dintre tipurile:
File,
InputStream, Readable, String, ReadableByteChannel.
Cele mai folosite metode sunt urmatoarele:
public boolean hasNext()întoarce true daca mai urmeaza o entitate;
public String next()detecteaza si întoarce urmatoarea entitate;
public boolean hasNextLine()întoarce true daca mai urmeaza o noua linie de intrare;
public String nextLine()întoarce urmatoarea linie de la intrare;
În metodele descrise în continuare, XXX poate fi:
Boolean Byte Short Int Long Float Double BigInteger BigDecimal
Metodele hasNextXXX(), cu semnificatie evidenta, pot lansa exceptia:
IllegalStateException
daca scanner-ul este
închis.
Metodele nextXXX(), cu semnificatie evidenta, pot lansa exceptiile:
InputMismatchException
- daca entitatea nu
este cea asteptata;
NoSuchElementException
- daca s-a ajuns la
sfârsitul intrarii;
IllegalStateException
- daca scanner-ul este
închis.
Metoda:
public void close()
închide scanner-ul.
|