Cursuri Laboratoare Index Java Home

Curs 3
Fluxuri (Intrari / Iesiri)




Ce sunt fluxurile?

Adeseori programele necesita citirea unor informatii care se gasesc pe o sursa externa sau trimiterea unor informatii catre o destinatie externa. Informatia se poate gasi oriunde : într-un fisier pe disc, în retea, în memorie sau în alt program si poate fi de orice tip: date primitive, obiecte, imagini, sunete, etc. Pentru a aduce informatii dintr-un mediu extern, un progam Java trebui sa deschida un canal de comunicatie (flux) catre sursa informatiilor (fisier, memorie, socket,etc) si sa citeasca serial informatiile respective:


Similar, un program poate trimite informatii catre o destinatie externa deaschizând un canal de comunicatie (flux) catre acea destinatie si scriind serial informatiile respective:


Indiferent de tipul informatiilor, citirea/scrierea informatiilor de pe/catre un mediu extern respecta urmatorii algoritmi:
Citirea Scrierea
deschide canal comunicatie 
while (mai sunt informatii) {
 	citeste informatie 
}	 
inchide canal comunicati;
deschide canal comunicatie 
while (mai sunt informatii) {
 	scrie informatie 
}	 
inchide canal comunicati;	
Pentru a generaliza, atât sursa externa a unor informatii cât si destinatia lor sunt vazute ca fiind niste procese care produc, respectiv consuma informatii:
Definitii:
Un flux este un canal de comunicatie unidirectional între doua procese.
Un proces care descrie o sursa externa de date se numeste proces producator.
Un proces care descrie o destinatie externa pentru date se numeste proces consumator.
Un flux care citeste date se numeste flux de intrare.
Un flux care scrie date se numeste flux de iesire.
Observatii:
Fluxurile sunt canale de comunicatie seriale pe 8 sau 16 biti.
Fluxurile sunt unidirectionale, de la producator la consumator
Fiecare flux are un singur proces producator si un singur proces consumator
Intre doua procese pot exista oricâte fluxuri, orice proces putând fi atât producator si consumator în acelasi timp, dar pe fluxuri diferite
Consumatorul si producatorul nu comunica direct printr-o interfata de flux ci prin intermediul codului Java de tratare a fluxurilor
Clasele si intefetele standard pentru lucu cu fluxuri se gasesc în pachetul java.io. Deci orice program care necesita operatii de intrare/iesire trebuie sa contina instructiunea de import a pachetului java.io: import java.io.*;


Clasificarea fluxurilor

Exista trei tipuri de clasificare a fluxurilor:
  1. Dupa "directia" canalului de comunicatie deschis fluxurile se împart în:
  2. Dupa tipul de date pe care opereaza:
  3. Dupa actiunea lor:


    Ierarhia claselor pentru lucrul cu fluxuri

    Fluxuri de caractere

    Clasele radacina pentru ierarhia claselor ce se ocupa cu fluxurile de caractere sunt Reader (pentru fluxuri de intrare) si Writer (pentru fluxuri de iesire). Acestea sunt superclase abstracte pentru clase ce implementeaza fluxuri specializate pentru citirea/scrierea datelor pe 16 biti.
    Ierarhia claselor pentru fluxuri de intrare pe caractere:


    Ierarhia claselor pentru fluxuri de iesire pe caractere:

    Au fost puse în evidenta (colorate cu gri) fluxurile care intra în categoria fluxurilor pentru procesarea datelor.

    Fluxuri de octeti

    Clasele radacina pentru ierarhia claselor ce se ocupa cu fluxurile de octeti sunt InputStream (pentru fluxuri de intrare) si OutputStream (pentru fluxuri de iesire). Acestea sunt superclase abstracte pentru clase ce implementeaza fluxuri specializate pentru citirea/scrierea datelor pe 8 biti.
    Ierarhia claselor pentru fluxuri de intrare pe octeti:


    Ierarhia claselor pentru fluxuri de iesire pe octeti:

    Au fost puse în evidenta (colorate cu gri) fluxurile care intra în categoria fluxurilor pentru procesarea datelor.
    Atentie: Pentru majoritatea programelor scrierea si citirea datelor se vor face prin intermediul fluxurilor de caractere deoarece acestea permit manipularea caracterelor Unicode (16-biti), în timp ce fluxurile de octeti permit doar lucrul pe 8 biti - caractere ASCII.


    Metode comune fluxurilor

    Superclasele abstracte Reader si InputStream definesc metode similare pentru citirea datelor.
    Reader InputStream
    int read()
    int read(char buf[])
    int read(char buf[], int offset,int length)
    
    int read()
    int read(byte buf[])
    int read(byte buf[], int offset,int length)
    
    De asemenea ambele clase pun la dispozitie metode pentru marcarea unei locatii într-un flux, saltul peste un numar de pozitii, resetarea pozitiei curente, etc.

    Superclasele abstracte Writer si OutputStream sunt de asemenea paralele, definind metode similare pentru scrierea datelor.
    Writer OutputStream
    int write()
    int write(char buf[])
    int write(char buf[], int offset,int length)
    
    int write()
    int write(byte buf[])
    int write(byte buf[], int offset,int length)
    


    Inchiderea oricarui flux se realizeaza prin metoda close. In cazul în care aceasta nu este apelata explicit fluxul va fi automat închis de catre colectorul de gunoaie atunci când nu va mai exista nici o referinta la el.
    Metodele referitoare la fluxuri pot genera exceptii de tipul IOException.


    Folosirea fluxurilor

    Asa cum am vazut fluxurile pot fi împartite în functie de activitatea lor, în fluxuri care se ocupa efectiv cu citirea/scrierea datelor si fluxuri pentru procesarea datelor. In continuare vom vedea care sunt cele mai importante clase din cele doua categorii si la ce folosesc acestea:

    Fluxuri pentru citirea/scrierea efectiva a datelor

    Clasele ce descriu fluxuri pentru citirea/scrierea efectiva a datelor pot fi împartite în functie de tipul sursei datelor astfel:
    Tip sursa Fluxuri caractere Fluxuri octeti
    Memorie CharArrayReader, CharArrayWriter ByteArrayInputStream, ByteArrayOutputStream
    Aceste fluxuri folosesc pentru scrierea/citirea informatiilor în memorie si sunt create pe un vector existent deja. Cu alte cuvinte permit tratarea vectorilor ca sursa/destinatie pentru crearea unor fluxuri de intrare/iesire.
    StringReader, StringWriter StringBufferInputStream
    Permit tratarea sirurilor de caractere aflate în memorie ca sursa/destinatie pentru crearea unor fluxuri de intrare/iesire. StringReader si StringWriter sunt folosite cu obiecte de tip String iar StringBufferInputStream cu obiecte de tip StringBuffer.
    Pipe PipedReader, PipedWriter PipedInputStream, PipedOutputStream
    Implementeaza componentele de intrare/iesire ale unei conducte de date (pipe). Pipe-urile sunt folosite pentru a canaliza iesirea unui program sau fir de executie catre intrarea altui program sau fir de executie (vezi "Conectarea fluxurilor").
    Fisier FileReader, FileWriter FileInputStream, FileOutputStream
    Numite si fluxuri fisier, acestea sunt folosite pentru citirea datelor dintr-un fisier, respectiv scrierea datelor într-un fisier (vezi "Fluxuri pentru lucrul cu fisiere").


    Fluxuri pentru procesarea datelor

    Clasele ce descriu fluxuri pentru procesarea datelor pot fi împartite în functie de tipul de procesare pe care îl efectueaza:
    Tip procesare Fluxuri caractere Fluxuri octeti
    "Bufferizare" BufferedReader,BufferedWriter BufferedInputStream,BufferedOutputStream
    Sunt folosite pentru a introduce un buffer în procesul de scriere/citire a informatiilor, reducând astfel numarul de accese la dispozitivul ce reprezinta sursa originala de date. Sunt mult mai eficiente decât fluxurile fara buffer si din acest motiv se recomanda folosirea lor ori de câte ori eset posibil (vezi "Citirea si scrierea cu zona tampon").
    Filtrare FilterReader, FilterWriter FilterInputStream, FilterOutputStream
    Sunt clase abstracte ce definesc o interfata pentru fluxuri care filtreaza automat datele citite sau scrise (vezi "Fluxuri pentru filtrare").
    Conversie
    octeti-caractere
    InputStreamReader, OutputStreamWriter  
    Formeaza o punte de legatura între fluxurile de caractere si fluxurile de octeti. Un flux InputStreamReader citeste octeti dintr-un flux InputStream ai îi converteate la caractere folosind codificarea standard a caracterelor sau o codificare specificata de program. Similar, un flux OutputStreamWriter converteste caractere în octeti si trimite rezutatul catre un flux de tipul OutputStream.
    Concatenare   SequenceInputStream
    Concateneaza mai multe fluxuri de intrare într-unul singur (vezi "Concatenarea fisierelor").
    Serializare   ObjectInputStream, ObjectOutputStream
    Folosite pentru serializarea obiectelor (vezi "Serializarea obiectelor").
    Conversie
    tipuri de date
      DataInputStream, DataOutputStream
    Folosite la scrierea/citirea datelor de tip primitiv într-un format independent de masina pe care se lucreaza (vezi "Folosirea claselor DataInputStream si DataOutputStream").
    Numarare LineNumberReader LineNumberInputStream
    Numara liniile citite de la un flux de intrare.
    Citire în avans PushbackReader PushbackInputStream
    Fluxuri de intrare care au un buffer de 1-caracter(octet) în care este citit în avans si caracterul (octetul) care urmeaza celui curent citit.
    Afisare PrintWriter PrintStream
    Metode convenabile pentru afisarea informatiilor.

    Crearea unui flux

    Orice flux este un obiect al clasei ce implementeaza fluxul respectiv. Crearea unui flux se realizeaza asadar similar cu crearea obiectelor prin instructiunea new().
    Exemple:
    	//crearea unui flux de intrare pe caractere
    	FileReader in = new FileReader("fisier_intrare.txt");
    
    	//crearea unui flux de iesire pe caractere
    	FileWriter out = new FileWriter("fisier_iesire.txt");
    
    	//crearea unui flux de intrare pe octeti
    	FileInputStream in = new FileInputStream("fisier_intrare.txt");
    
    	//crearea unui flux de iesire pe octeti
    	FileOutputStrem out = new FileOutputStream("fisier_iesire.txt");
    
    Asadar, crearea unui flux primitiv de date care scrie/citeste informatii de la un dispozitiv extern are formatul general:
    
    	FluxPrimitiv numeFlux = new FluxPrimitiv( dispozitiv extern )
    
    Fluxurile de procesare nu pot exista de sine statatoare ci se suprapun pe un flux primitiv de citire/scriere a datelor. Din acest motiv constructorii claselor pentru fluxurile de procesare nu primesc ca argument un dispozitiv extern de memorare a datelor ci o referinta la un flux primitiv responsabil cu citirea/scrierea efectiva a datelor:
    Exemple:
    	//crearea unui flux de intrare printr-un buffer
    	BufferedReader in  = new BufferedReader(new FileReader("fisier.in"));
    	//echivalent cu
    	FileReader fr = new FileReader("fisier.in");
    	BufferedReader in  = new BufferedReader(fr);
    
    	//crearea unui flux de iesire printr-un buffer
    	BufferedWriter out = new BufferedWriter(new FileWriter("fisier.out")));
    	//echivalent cu
    	FileWriter fo = new FileWriter("fisier.out");
    	BufferedWriter out = new BufferedWriter(fo);
    
    Asadar, crearea unui flux pentru procesarea datelor are formatul general:
    
    	FluxProcesare numeFlux = new FluxProcesare( referintaFluxPrimitiv )
    
    In general, fluxurile pot fi grupate în succesiuni oricât de lungi:
    	DataInputStream in =
    		new DataInputStream(
    			new BufferedInputStream(
    				new FileInputStream("fisier.in")));
    



    Fluxuri pentru lucrul cu fisiere (fluxuri de tip "File")

    Fluxurile pentru lucrul cu fisiere sunt cele mai usor de înteles. Clasele care implementeaza aceste fluxuri sunt urmatoarele:
    
    		FileReader, FileWriter			- caractere
    		FileInputStream, FileOutputStream 	- octeti
    
    Constructorii acestor clase accepta ca argument un obiect care sa specifice un anume fisier. Acesta poate fi un sir de caractere, on obiect de tip File sau un obiect de tip FileDesciptor (vezi "Clasa File").
    Constructorii clasei FileReader:
    	public FileReader( String fileName ) throws FileNotFoundException
    	public FileReader( File file ) throws FileNotFoundException
    	public FileReader( FileDescriptor fd )
    
    Constructorii clasei FileWriter:
    	public FileWriter( String fileName ) throws IOException
    	public FileWriter( File file ) throws IOException
    	public FileWriter( FileDescriptor fd )
    	public FileWriter( String fileName, boolean append ) throws IOException
    
    Cei mai uzuali constructori sunt cei care primesc ca argument numele fisierului. Acestia pot provoca exceptii de tipul FileNotFoundException în cazul în care fisierul cu numele specificat nu exista. Din acest motiv orice creare a unui flux de acest tip trebuie facuta într-un bloc try sau metoda în care sunt create fluxurile respective trebuie sa arunce exceptiile de tipul FileNotFoundException sau de tipul superclasei IOException.
    Exemplu: un program care copie con]inutul unui fisier în alt fisier:
    
    import java.io.*;
    public class Copy {
    	public static void main(String[] args) throws IOException {
    
    		FileReader in = new FileReader("in.txt");
    		FileWriter out = new FileWriter("out.txt");
    
    		int c;
    		while ((c = in.read()) != -1)
    			out.write(c);
    
    		in.close();
    		out.close();
    	}
    }
    
    Obs: metoda main arunca exceptii IOException care este superclasa pentru FileNotFoundException. Aceste exceptii nu vor "prinse" decât de interpretor si va fi afisat un mesaj de eroare la aparitia lor.


    Citirea si scrierea cu zona tampon

    Clasele pentru citirea/scrierea cu zona tampon sunt:
    
    		BufferedReader, BufferedWriter			- caractere
    		BufferedInputStream, BufferedOutputStream	- octeti
    
    Sunt folosite pentru a introduce un buffer în procesul de scriere/citire a informatiilor, reducând astfel numarul de accese la dispozitivul ce reprezinta sursa originala de date. Sunt mult mai eficiente decât fluxurile fara buffer si din acest motiv se recomanda folosirea lor ori de câte ori este posibil.
    Clasele BufferedReader si BufferedInputStream citesc în avans date si le memoreaza într-o zona tampon (buffer). Atunci când se executa o operatie read(), octetul citit va fi preluat din buffer. In cazul în care buffer-ul este gol citirea se face direct din flux si, odata cu citirea octetului, vor fi memorati în buffer si octetii care îi urmeaza.
    Similar, se lucreaza si cu clasele BufferedWriter si BufferedOutputStream.
    Fluxurile de citire/scriere cu buffer sunt fluxuri de procesare si sunt folosite prin suprapunere cu alte fluxuri.
    Exemplu:
    	BufferedOutputStream out = new BufferedOutputStream(
    		new FileOutputStream("out.dat"), 1024)
    
    Constructorii acestor clase sunt urmatorii:
    BufferedReader BufferedReader( Reader in )
    BufferedReader( Reader in, int dim_buffer )
    BufferedWriter BufferedWriter( Writer out )
    BufferedWriter( Writer out, int dim_buffer )
    BufferedInputStream BufferedInputStream( InputStream in )
    BufferedInputStream( InputStream in, int dim_buffer )
    BufferedOutputStream BufferedOutputStream( OutputStream out )
    BufferedOutputStream( OutputStream out, int  dim_buffer )
    In cazul constructorilor în care dimensiunea buffer-ului nu este specificata, aceasta primeste valoarea implicita de 512 octeti.

    Metodele acestor clase sunt cele uzuale de tipul read si write (vezi "Metode comune fluxurilor"). Pe lânga acestea, clasele pentru scriere prin buffer mai au si metoda flush care goleste explicit zona tampon chiar daca aceasta nu este plina.
    Exemplu:
    	BufferedWriter out = new BufferedWriter( 
    		new FileWriter("out.dat"), 1024)
    	//am creat un flux cu buffer de 1024 octeti
    	for(int i=0; i<1024; i++)
    		out.write(i);
    	//bufferul nu este plin -> in fisier nu s-a scris nimic
    	out.flush(); 
    	//bufferul este golit -> datele se scriu în fisier
    

    Metoda readLine

    BufferedReader br = new BufferedReader(new FileReader("in"))	
    String input;
    while ((input = br.readLine()) != null) {
    	. . .
    	//readLine metoda specifica care citeste o linie
    }
    
    Metoda readLine permite citirea unui flux linie cu linie.


    Concatenarea fisierelor

    Clasa SequenceInputStream permite unei aplicatii sa combine serial mai multe fluxuri de intrare astfel încât acestea sa apara ca un singur flux de intrare. Citirea datelor dintr-un astfel de flux se face astfel: se citeste din primul flux de intrare specificat pâna când se ajunge la sfârsitul acestuia, dupa care primul flux de intrare este închis si se deschide automat urmatorul flux de intrare din care se vor citi în continuare datele, dupa care procesul se repeta pâna la terminarea tuturor fluxurilor de intrare.
    Constructorii acestei clase sunt:
    SequenceInputStream( Enumeration e ) Construieste un flux secvential dintr-o multime de fluxuri de intrare. Fiecare obiect în enumerarea primita ca parametru trebuie sa fie de tipul InputStream.
    SequenceInputStream( InputStream s1, InputStream s2 ) Construieste un flux de intrare care combina doua fluxuri s1 si s2. Primul flux citit va fi s1.

    Exemplul cel mai elocvent de folosirea a acestei clase este concatenarea a doua fisiere:
    //Concatenarea a 2 fisiere ale caror nume sunt primite la linia de comanda
    //Rezultatul concatenarii este afisat pe ecran
    import java.io.*;
    public class Concatenare1 {
    
    	public static void main(String args[]) throws IOException {
    		FileInputStream f1 = new FileInputStream(args[0]);
    		FileInputStream f2 = new FileInputStream(args[1]);
    
    		SequenceInputStream s = new SequenceInputStream(f1, f2);
    		int c;
    
    		while ((c = s.read()) != -1)
    			System.out.write(c);
    
    		s.close();
    		//f1 si f2 sunt închise automat
    	}//main
    }//class
    
    Pentru concatenarea mai multor fisiere exista doua variante


    Fluxuri pentru filtrarea datelor

    Un flux de filtrare se ataseaza altui flux pentru a filtra datele care sunt citite/scrise de catre acel flux. Clasele pentru fluxuri de filtrare au ca superclase clasele abstracte FilterInputStream (pentru filtrarea fluxurilor de intrare) si FilterOutputStream (pentru filtrarea fluxurilor de iesire).
    Clasele pentru filtrarea datelor sunt:
    
    		DataInputStream, DataOutputStream 
    		BufferedInputStream, BufferedOutputStream 
    		LineNumberInputStream 
    		PushbackInputStream 
    		PrintStream (flux de iesire) 
    
    Observati ca toate aceste clase descriu fluxuri de octeti.
    Filtrarea datelor nu trebuie vazuta ca o metoda de a elimina anumiti octeti dintr-un flux ci de transforma acesti octeti în date care sa poata fi interpretate sub alta forma.
    Asa cum am vazut la citirea/scrierea cu zona tampon clasele de filtrare BufferedInputStream si BufferedOutputStream grupeaza datele unui flux într-un buffer, urmând ca citirea/scrierea sa se faca prin intermediu acelui buffer (vezi "Citirea si scrierea cu zona tampon").
    Asadar fluxurile de filtrare nu elimina date citite sau scrise de un anumit flux, ci introduc o noua modalitate de manipulare a lor. Din acest motiv fluxurile de filtrare vor contine anumite metode specializate pentru citirea/scrierea datelor, altele decât cele comune tuturor fluxurilor (metode de tip read/write).
    Folosirea fluxurilor de filtrare se face prin atasarea lor de un flux care se ocupa efectiv de citirea/scrierea datelor:
    
    	FluxFiltrare numeFlux = new FluxFiltrare ( referintaAltFlux )
    
    Cele mai importante clase din aceasta categorie sunt DataInputStream si DataOutputStream

    Clasele DataInputStream si DataOutputStream

    Aceste clase ofera metode prin care un flux nu mai este vazut ca o însiruire de octeti, ci ca o sursa de date primitive. Prin urmare vor furniza metode pentru citirea si scrierea datelor la nivel de tip de data si nu la nivel de octet. Constructorii si metodele cele mai importante (altele decât read/write) sunt date în tabelul de mai jos :
    DataInputStream DataOuputStream
    //Constructor
    DataInputStream(InputStream in)
    //Constructor
    DataOutputStream(OutputStream out)
    readBoolean( )
    readByte( )
    readChar( )
    readDouble( )
    readFloat( )
    readInt( )
    readLong( )
    readShort( )
    readUnsignedByte( )
    readUnsignedShort( )
    String readUTF( )
    
    writeBoolean( boolean v )
    writeByte( int v )
    writeChar( int v )
    writeDouble( double v )
    writeFloat( float v )
    writeInt( int v )
    writeLong( long v )
    writeShort( int v )
    writeBytes( String s )
    writeChars( String s )
    writeUTF( String str )  	
    

    Aceste metode au denumirile generice de readXXX si writeXXX specificate de interfetele DataInput si DataOutput. Pot provoca exceptii de tipul IOException.
    Atentie: Un fisier în care au fost scrise informatii folosind metode writeXXX nu va putea fi citit decât prin metode readXXX.


    Fluxuri standard de intrare/iesire

    Mergând pe linia introdusa de sistemul de operare UNIX orice program Java are : In general intrarea standard este tastatura iar iesirea standard este ecranul.
    Intrarea si iesirea standard sunt de fapt niste obiecte pre-create ce descriu fluxuri de date pentru citirea respectiv scrierea la dispozitivele standard ale sistemului. Aceste obiecte sunt definite publice în clasa System si sunt:
    Variabila Semnificatie Tip flux
    System.in flux standard de intrare InputStream
    System.out flux standard de iesire PrintStream
    System.err flux standard pentru afisarea erorilor PrintStream

    Afisarea informatiilor pe ecran

    Am vazut deja numeroase exemple de folosire a fluxului standard de iesire, el fiind folosit la afisarea oricaror rezultate pe ecran (în modul consola):System.out.println("mesaj").
    Fluxul standard pentru afisarea erorilor se foloseste similar:
    	catch (IOException e) {
    		System.err.println("Eroare de intrare/iesire!")
    	}
    
    Fluxurile de iesire pot fi folosite asadar fara probleme deoarece tipul lor este PrintStream, clasa primitiva pentru scrierea efectiva a datelor. In schimb fluxul standard de intrare System.out este de tip InputStream care este o clasa abstracta, deci pentru a-l putea utiliza va trebui sa-l folosim împreuna cu un flux de procesare a datelor sau cu orice alt flux ce permite citirea efectiva a datelor.

    Citirea datelor de la tastatura

    Uzual vom dori sa folosim metoda readLine pentru citirea datelor de la tastatura si din acest motiv vom folosi intrarea standard împreuna cu o clasa de procesare care implementeaza metoda readLine. Exemplul tipic este:
    	BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
    	System.out.print("Introduceti o linie:");
    	String linie = stdin.readLine()
    	System.out.println(linie);
    
    Exemplu: un program care afiseaza liniile introduse de la tastatura
    import java.io.*;
    public class Echo {
    		public static void main(String[] args) {
    		BufferedReader stdin = new BufferedReader(
    			new InputStreamReader(System.in));
    		String s;
    		try {
    			while((s = stdin.readLine()).length() != 0)
    				System.out.println(s);
    			//Programul se opreste cu o linie vida
    		} catch(IOException e) {e.printStackTrace();}
    	}
    }
    
    Observatie: Metoda readLine poate provoca exceptii de tipul IOException.

    Redirectarea intrarii/iesirii standard

    Incepând cu Java 1.1 au fost introduse în clasa System metode statice pentru a redirecta fluxurile de intrare si iesire standard. Aceste metode sunt:
    	setIn(InputStream)	- redirectare intrare
    	setOut(PrintStream)	- redirectare iesire
    	setErr(PrintStream)	- redirectare erori 
    
    Redirectarea iesirii este utila în special atunci când sunt afisate foarte multe date pe ecran si acestea se deruleaza mai repede decât putem citi. Putem redirecta afisarea catre un fisier pe care sa-l citim dupa executia programului. Secventa clasica de redirectare a iesirii este:
    	PrintStream out = new PrintStream(new BufferedOutputStream(
    			new FileOutputStream("rezultate.out")));
    	System.setOut (out);
    
    Redirectarea erorilor într-un fisier poate fi de asemenea utila.
    	PrintStream err = new PrintStream(new BufferedOutputStream(
    			new FileOutputStream("erori.err")));
    	System.setErr (err);
    
    Redirectarea intrarii poate fi utila pentru un program consola care primeste niste valori de intrare. Pentru a nu le scrie de la tastatura de fiecare data în timpul testarii programului ele pot fi puse într-un fisier, redirectând intrarea standard. In momentul când testarea programului a luat sfârsit redirectarea poate fi eliminata, datele fiind cerute din nou de la tastatura.
    Exemplu de folosire a redirectarii:
    import java.io.*;
    class Redirectare {
    	public static void main(String[] args) {
    		try {
    			BufferedInputStream in = new BufferedInputStream(
    				new FileInputStream("Redirectare.java"));
    			PrintStream out =	new PrintStream(new BufferedOutputStream(
    				new FileOutputStream("test.out")));
    			System.setIn(in);
    			System.setOut(out);
    			System.setErr(out);
    
    			BufferedReader br = new BufferedReader(
    					new InputStreamReader(System.in));
    			String s;
    			while((s = br.readLine()) != null)
    				System.out.println(s);
    			out.close(); // Atentie!
    		} catch(IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
    



    Analiza lexicala pe fluxuri (clasa StreamTokenizer)

    Clasa StreamTokenizer parcurge un flux de intrare de orice tip si îl împarte în "atomi lexicali". Rezultatul va consta în faptul ca în loc sa se citeasca octeti sau caractere se vor citi, pe rând, atomii lexicali ai fluxului respectiv.
    Printr-un atom lexical se în]elege în general: Atomii lexicali sunt despartiti între ei de separatori. Implicit acesti separatori sunt cei obisnuti( spatiu, tab, virgula, punct si virgula), însa pot fi schimbati prin diverse metode ale clasei.
    Constructorii acestei clase sunt:
    		public StreamTokenizer( Reader r )
    		public StreamTokenizer( InputStream is )
    
    Identificarea tipului si valorii unui atom lexical se face prin intermediul variabilelor:
    		TT_EOF	- atom ce marcheaz sfârsitul fluxului
    		TT_EOL	- atom ce marcheaz sfârsitul unei linii
    		TT_NUMBER  	- atom de tip numar
    		TT_WORD 	- atom de tip cuvânt
    		nval		- valoarea unui atom numeric
    		sval		- sirul continut de un atom de tip cuvânt
    		ttype		- tipul ultimului atom citit din flux
    
    Citirea atomilor din flux se face cu metoda nextToken(), care returneza tipul atomului lexical citit si scrie în variabilele nval sau sval valoarea corespunzatoare atomului.
    Exemplul tipic de folosire a unui analizor lexical este citirea unei secvente de numere si siruri aflate într-un fisier sau primite de la tastatura:
    //Citirea unei secvente de numere si siruri
    import java.io.*;
    public class TestTokenizer {
    	public static void  main(String args[]) throws IOException{
    		FileInputStream fis = new FileInputStream("test.dat");
    		BufferedReader br = new BufferedReader(new InputStreamReader(fis));					
    		StreamTokenizer st = new StreamTokenizer(br);
    
    		int tip = st.nextToken();	//citesc primul atom lexical
    		while (tip != StreamTokenizer.TT_EOF) {
    			switch (tip) {
    			  case StreamTokenizer.TT_WORD :	//cuvant
    				System.out.println(st.sval);
    				break;
    			  case StreamTokenizer.TT_NUMBER : 	//numar
    				System.out.println(st.nval);
    			}
    			tip = st.nextToken();//urmatorul atom
    		}
    	}
    }
    
    Asadar, modul de utilizare tipic pentru un analizor lexical este într-o bucla "while" în care se citesc atomii unul câte unul cu metoda nextToken pâna se ajunge la sfârsitul fluxului (TT_EOF). In cadrul buclei "while" se afla tipul atomul curent curent (întors de metoda nextToken) si apoi se afla valoarea numerica sau sirul de caractere corespunzator atomului respectiv.
    Un exemplu mai simplu de folosire (dar nepractic) ar fi citirea unui întreg sau a unui sir de caractere de la tastatura:
    //Citirea unui întreg de la tastatura
    import java.io.*;
    public class TestReadIn {
    	public static void main(String args[]) {
    	try{
    		Reader r = new InputStreamReader(System.in);
    		StreamTokenizer st = new StreamTokenizer(r);
    		System.out.print("n=");
    		int tip = st.nextToken();
    		System.out.println("Valoarea lui n este " + (int)st.nval);
    	}
    	catch (IOException e) {}
    	}
    }
    



    Alte clase pentru lucrul cu fisiere

    Clasa RandomAccesFile (fisiere cu acces direct)

    Fluxurile sunt, asa cum am vazut procese secventiale de intrare/iesire. Acestea sunt adecvate pentru scrierea/citirea de pe medii secventiale de memorare a datelor cum ar fi banda magnetica,etc. desi sunt foarte utile si pentru dispozitive în care informatia poate fi accesata direct.
    Clasa RandomAccesFile: Constructorii acestei clase sunt:
    	RandomAccessFile(String numeFisier,String mod_acces) throws IOException
    	RandomAccessFile(String fisier,String mod_acces) throws IOException
    	unde mod_acces poate fi:
    		"r"	- fisierul este deschis numai pentru citire (read-only)
    		"rw"	- fisierul este deschis pentru citire si scriere (read-write)
    
    Exemple:
    	RandomAccesFile f1 = new RandomAccessFile("fisier.txt", "r");
    	//deschide un fisier pentru citire
    
    	RandomAccesFile f2 = new RandomAccessFile("fisier.txt", "rw");
    	//deschide un fisier pentru scriere si citire
    
    Clasa RandomAccesFile suporta notiunea de pointer de fisier. Acesta este un indicator ce specifica pozitia curenta în fisier. La deschiderea unui fisier pointerul are valoarea 0, indicând începutul fisierului. Apeluri la metode readXXX sau writeXXX deplaseaza pointerul fisierului cu numarul de octeti cititi sau scrisi de metodele respective.


    In plus fata de metodele de citire/scriere clasa pune la dispozitie si metode pentru controlul pozitiei pointerului de fisier. Acestea sunt:
    int skipBytes ( int n ) Muta pointerul fisierului înainte cu un numar specificat de octeti
    void seek (long pozitie) Pozitioneaza pointerului fisierului înaintea octetului specificat.
    long getFilePointer ( ) Returneaza pozitia pointerului de fisier (pozitia de la care se citeste/la care se scrie)


    Clasa File

    Clasa File are un nume înselator, întrucât ea nu se refera doar la un fisier ci poate reprezenta fie un fisier anume, fie multimea fisierelor dintr-un director. O instanta a acestei clase poate sa reprezinte asadar: un fisier sau un director.
    Specificarea unui fisier/director se face prin specificare caii absolute spre acel fisier sau a caii relative fata de directorul curent. Acestea trebuie sa respecte conventiile de specificare a cailor si numelor fisierelor de pe masina gazda.
    Utilitate clasei File consta în furnizarea unei modalitati de a abstractiza dependentele cailor si numelor fisierelor fata de masina gazda precun si punerea la dispozitie a unor metode pentru lucrul cu fisere si directoare la nivelul sistemului de operare.
    Astfel, în aceasta clasa vom gasi metode pentru testarea existentei, stergerea, redenumirea unui fisier sau director, crearea unui director, listarea fisierelor dintr-un director, etc.
    Trebuie mentionat si faptul ca majoritatea constructorilor fluxurilor care permit accesul la fisiere accepta ca argument un obiect de tip File în locul unui sir ce reprezinta numele fisierului accesat.
    		File f_in = new File("fisier.txt");
    		FileInputStream st_in = new FileInputStream(f_in)
    
    Cel mai uzual constructor al clasei File este: public File( String  fisier)
    Metodele mai importante ale clasei File sunt:
    boolean isDirectory( )
    boolean isFile( )
    
    Testeaza daca un obiect File>/tt> reprezinta un fisier sau un director
    String getName( )
    String getPath( )
    String getAbsolutePath()
    String getParent()
    
    Afla numele (fara cale), calea fisierului sau directorului reprezentat de obiectul respectiv
    boolean exists( )
    boolean delete( )
    boolean mkdir( )
    boolean mkdirs( )
    boolean renameTo(File dest)
    
    Testeaza daca exista un anumit fisier/director
    Sterge fisierul/directorul reprezentat de obiect
    Creeaza un director
    Creeaza o succesiune de directoare
    Redenumeste un fisier/director
    String[] list( ) 
    String[] list (FilenameFilter filter )
    
    Creeaza o lista cu numele fisierelor dintr-un director
    Creeaza o lista cu numele fisierelor dintr-un director filtrate dupa un anumit criteriu specificat.(vezi "Interfata FilenameFilter")
    boolean canRead( )
    boolean canWrite( )
    
    Testeaza daca un anumit fisier poate fi folosit pentru citire, respectiv scriere
    long length( )
    long lastModified( )
    
    Afla lungimea si data ultimei modificari a unui fisier.


    Exemplu1: listarea fisierelor din directorul curent
    //Listarea fisierelor din directorul curent
    import java.io.*;
    public class DirList {
    	public static void main(String[] args) {
    		try {
    			File director = new File(".");
    			String[] list;
    			list = director.list();
    
    			for(int i = 0; i < list.length; i++)
    				System.out.println(list[i]);
    		} catch(Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    Exemplu2: aflarea informatiilor despre un fisier
    //Afiseaza informatii despre fisierele primite la linia de comanda
    import java.io.*;
    public class FileInfo {
    	private static void fileInfo(File f) {
    		System.out.println(
    			"Cale absoluta: " + f.getAbsolutePath() +
    			"\n Poate citi: " + f.canRead() +
    			"\n Poate scrie: " + f.canWrite() +
    			"\n Nume: " + f.getName() +
    			"\n Parinte: " + f.getParent() +
    			"\n Cale: " + f.getPath() +
    			"\n Lungime: " + f.length() +
    			"\n Data ultimei modificare: " + f.lastModified());
    		if(f.isFile())
    			System.out.println("Este fisier");
    		else if(f.isDirectory())
    			System.out.println("Este director");
    	}
    
    	public static void main(String[] args) {
    		for(int i=0; i < args.length; i++)
    			fileInfo ( new File(args[i]) );
    		
    	}  
    }
    



    Interfata FilenameFilter

    vezi "Interfete"


    Cursuri Laboratoare Index Java Home