Dupa actiunea lor:
- fluxuri primare de citire/scriere a datelor (se ocupa efectiv cu citirea/scrierea datelor)
- fluxuri pentru procesarea datelor
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
- folosirea unei enumerari - primul constructor
- concatenarea pe rând a acestora folosind al 2-lea constructor; concatenarea a 3 fisiere
va construi un flux de intrare astfel:
FileInputStream f1 = new FileInputStream(args[0]);
FileInputStream f2 = new FileInputStream(args[1]);
FileInputStream f3 = new FileInputStream(args[2]);
SequenceInputStream s = new SequenceInputStream(
f1, new SequenceInputStream(f2, f3));
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 :
- o intrare standard
- o iesire standard
- o iesire standard pentru erori
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:
- un identificator (un sir care nu este între ghilimele)
- un numar
- un sir de caractere
- un comentariu
- un separator
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:
- permite accesul nesecvential (direct) la continutul unui fisier.
- este o clasa de sine statatoare, subclasa directa a clasei Object.
- se gaseste în pachetul java.io.
- implementeaza interfetele DataInput si DataOutput, ceea ce înseamna ca
sunt disponibile metode de tipul readXXX, writeXXX
(vezi "Clasele DataInputStream si DataOutputStream").
- permite atât citirea cât si scriere din/in fisiere cu acces direct
- permite specificarea modului de acces al unui fisier (read-only, read-write)
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"