laborator 3 - manipularea datelor prin fluxuri java

13
Laborator 3 - Manipularea datelor prin fluxuri Java. 1. Fluxuri Java 2. Fluxuri pentru lucrul cu fisiere 3. Clasele DataInputStream si DataOutputStream 4. Fluxuri standard de intrare/iesire (tastatura/consola) 5. Utilizarea claselor StringTokenizer si StreamTokenizer 6. Clasa File 7. Comprimare Run-Length Encoding(RLE) 7.1. Introducere 7.2. Construirea unui flux filtru de iesire RLE 7.3. Construirea unui flux filtru de intrare RLE Fluxuri Java Fluxurile Java pun la dispozitie modalitatea prin care o aplicatie permite citirea unor informatii care se gasesc pe o sursa externa, respectiv 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. Mai mult, prin fluxuri este posibila comunicarea între doua sau mai multe fire de executie ale aceleiasi aplicatii. Fluxurile sunt secvente de octeti.Indiferent de tipul informatiilor, citirea/scrierea informatiilor de pe un mediu extern, respectiv pe un mediu extern respecta urmatorii algoritmi: Citirea deschide canal comunicatie; cat timp (mai sunt informatii) { citeste informatie; } inchide canal comunicatie; Scrierea deschide canal comunicatie cat timp (mai sunt informatii) { scrie informatie; } inchide canal comunicatie; Toate interfetele pentru fluxuri implementeaza un set de metode de baza, comune tuturor categoriilor de fluxuri. Metodele standard pentru lucrul cu fluxuri se gasesc în pachetul java.io. Biblioteca Java I/O furnizeaza numeroase clase de fluxuri. Toate clasele de fluxuri de intrare sunt subclase ale clasei abstracte InputStream , iar clasele de fluxuri de iesire sunt subclase ale clasei abstracte OutputStream. O execeptie o reprezinta clasa RandomAccessFile, care poate fi atat sursa, cat si destinatie. De aceea, aplicatiile pot citi si scrie intr- un obiect RandomAccessFile. Fluxuri de intrare Urmatoarele metode care se regasesc ca functii membre ale tuturor claselor ce implementeaza fluxurile de intrare : - read() - citeste date dintr-un flux de intrare - skip() - ignora unele date din fluxul de intrare

Upload: dohanh

Post on 29-Jan-2017

251 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Laborator 3 - Manipularea datelor prin fluxuri Java

Laborator 3 - Manipularea datelor prin fluxuri Java. 1. Fluxuri Java 2. Fluxuri pentru lucrul cu fisiere 3. Clasele DataInputStream si DataOutputStream 4. Fluxuri standard de intrare/iesire (tastatura/consola) 5. Utilizarea claselor StringTokenizer si StreamTokenizer 6. Clasa File 7. Comprimare Run-Length Encoding(RLE) 7.1. Introducere 7.2. Construirea unui flux filtru de iesire RLE 7.3. Construirea unui flux filtru de intrare RLE

Fluxuri Java Fluxurile Java pun la dispozitie modalitatea prin care o aplicatie permite citirea unor informatii care se gasesc pe o sursa externa, respectiv 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. Mai mult, prin fluxuri este posibila comunicarea între doua sau mai multe fire de executie ale aceleiasi aplicatii. Fluxurile sunt secvente de octeti.Indiferent de tipul informatiilor, citirea/scrierea informatiilor de pe un mediu extern, respectiv pe un mediu extern respecta urmatorii algoritmi: Citirea deschide canal comunicatie; cat timp (mai sunt informatii) { citeste informatie; } inchide canal comunicatie; Scrierea deschide canal comunicatie cat timp (mai sunt informatii) { scrie informatie; } inchide canal comunicatie; Toate interfetele pentru fluxuri implementeaza un set de metode de baza, comune tuturor categoriilor de fluxuri. Metodele standard pentru lucrul cu fluxuri se gasesc în pachetul java.io. Biblioteca Java I/O furnizeaza numeroase clase de fluxuri. Toate clasele de fluxuri de intrare sunt subclase ale clasei abstracte InputStream , iar clasele de fluxuri de iesire sunt subclase ale clasei abstracte OutputStream. O execeptie o reprezinta clasa RandomAccessFile, care poate fi atat sursa, cat si destinatie. De aceea, aplicatiile pot citi si scrie intr-un obiect RandomAccessFile. Fluxuri de intrare Urmatoarele metode care se regasesc ca functii membre ale tuturor claselor ce implementeaza fluxurile de intrare : - read() - citeste date dintr-un flux de intrare - skip() - ignora unele date din fluxul de intrare

Page 2: Laborator 3 - Manipularea datelor prin fluxuri Java

- markAvailable() - testeaza daca metoda mark() este disponibila pentru fluxul de intrare respectiv - close() - închide un flux de intrare Read int read(); int read(byte[] buffer) int read(byte[] buffer, int offset, int length) Skip long skip(long numar_octeti) public long skipN(int n) { int recordSize = 1024 ; long retNum = 0 ; try { //sare peste n inregistrari retNum = MyStream.skip(n * recordSize); //calc nr. de inregistrari peste care a sarit if retNum > 0 { retNum /= recordSize ; } catch (IOException e) { System.out.println.(e.getMessage()); } return (retNum); } Close void close(); public long closeStream() { try { MyStream.close(); } catch (IOException e) { System.out.println("Eroare la inchiderea fluxului"); } } Fluxuri de iesire Metode pentru fluxurile de iesire - write() - scrie date într-un flux de iesire - flush() - forteaza scrierea datelor într-un canal de redirectare - close() - închide un flux de iesire Write int write (); int write (byte[] buffer) int write (byte[] buffer, int offset, int length) Flush - forteaza scrierea catre dispozitivul de iesire a datelor stocate în zona tampon pentru un flux de iesire. void flush();

Page 3: Laborator 3 - Manipularea datelor prin fluxuri Java

Close - închide un flux de iesire void close();

Fluxurile pentru lucrul cu fisiere 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 FileDescriptor. 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: Acest program copiaza caracter cu caracter continutul unui fisier în alt fisier: //fisier sursa java Flux.java import java.io.*; public class Flux { public static void main(String args[]) { FileInputStream in; //in este flux de intrare FileOutputStream out; //out este flux de iesire int ok; try { in = new FileInputStream(args[0]); try { out = new FileOutputStream(args[1]); ok = 0; //citesc fisierul caracter cu caracter while(ok != -1) { try {

Page 4: Laborator 3 - Manipularea datelor prin fluxuri Java

ok = in.read(); out.write(ok); System.out.print((char)ok); } catch (IOException e) { System.out.println(e.getMessage()); System.exit(1); } } //while }//try catch (IOException e) { System.out.println(e.getMessage()); System.exit(1); } }//try catch (FileNotFoundException e) { System.out.println(e.getMessage()); System.exit(1); } }//main }//clasa Flux

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 sunt urmatoarele: DataInputStream DataInputStream(InputStream in) //Constructor readBoolean( ) readByte( ) readChar( ) readDouble( ) readFloat( ) readInt( ) readLong( ) readShort( ) readUnsignedByte( ) readUnsignedShort( ) String readUTF( ) DataOuputStream DataOutputStream(OutputStream out) //Constructor writeBoolean( boolean v ) writeByte( int v ) writeChar( int v ) writeDouble( double v ) writeFloat( float v ) writeInt( int v ) writeLong( long v ) writeShort( int v )

Page 5: Laborator 3 - Manipularea datelor prin fluxuri Java

writeBytes( String s ) writeChars( String s ) writeUTF( String str ) Aceste metode au denumirile generice de readXXX si writeXXX specificate de interfeteleDataInput si DataOutput. Pot provoca exceptii de tipul IOException.

Fluxuri standard de intrare/iesire Orice program Java poate are : - intrare standard - iesire standard - 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: System.in - flux standard de intrare; clasa InputStream System.out - flux standard de iesire;clasa PrintStream System.err - flux standard pentru afisarea erorilor;clasa PrintStream Afisarea informatiilor pe ecran Pentru afisarea informatiilor pe ecran se foloseste fluxul standard de iesire; el este 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.in 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 Daca dorim sa citim o intreaga linie de la tastatura putem folosi 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. Exemplu: //fisier sursa java Linie.java import java.io.*; public class Linie { public static void main(String[] args) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); String line; try {

Page 6: Laborator 3 - Manipularea datelor prin fluxuri Java

while((line = stdin.readLine()).length() != 0) System.out.println(line); //Programul se opreste cu o linie vida } catch(IOException e) {e.printStackTrace();} } } Redirectarea intrarii/iesirii standard Clasa System are 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 iesiriieste: PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("afisare.out"))); System.setOut(out); 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: //fisier sursa java Redirectare.java 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 line; while((s = br.readLine()) != null) System.out.println(line);

Page 7: Laborator 3 - Manipularea datelor prin fluxuri Java

out.close(); } catch(IOException e) { e.printStackTrace(); } } }

Utilizarea claselor StringTokenizer si StreamTokenizer

Biblioteca Java.uitl defineste clasa StringTokenizer care faciliteaza separarea unui sir de caractere in simboluri. Pentru a separa un sir de caractere, trebuie sa cream un obiect StringTokenizer, specificand sirul si caracterul special, utilizat de sir pentru a indica(delimita componenetele). Constructorii acestei clase sunt: StringTokenizer(String str, String delim, boolean returnSimbol) StringTokenizer(String str, String delim) StringTokenizer(String str) Parametrul str specifica sirul de caractere pe care doriti sa il separati in simboluri. Parametrul delim specifica o lista de caractere de delimitare. De exemplu, un argument delim "!?" indica faptul ca delimitatorii sunt semnul exclamarii si semnul intrebarii. In mod implicit, clasa StringTokenizer stabileste ca delimitator caracterele "\t\n\r" care corespund unui spatiu de tabulare, unui caracater de linie noua si unui caractere retur de car. Parametrul returnSimbol arata daca doriti sau nu ca delimitatorul sa fie returnat. Implicit, metoda nu returneaza delimitatorul ca simbol. Parcurgerea unei liste de simboluri Extragerea fiecarui simbol se face utilizand metoda nextToken: String nextToken() Pentru a afla daca obiectul StringTokenizer contine mai multe simboluri se poate apela metoda hasMoreTokens: boolean hasMoreTokens() In cazul in care dorim sa determinam numarul de simboluri pe care le contine obiectul StringTokenizer se poate apela metoda countTokens: int countTokens() 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 )

Page 8: Laborator 3 - Manipularea datelor prin fluxuri Java

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 Ca si-n cazul clasei StringTokenizer, 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. Exemplu: //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 } } }

Clasa File Clasa File 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 introducerea 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.

Page 9: Laborator 3 - Manipularea datelor prin fluxuri Java

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. In plus 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("fis.in"); FileInputStream st_in = new FileInputStream(f_in) Metodele mai importante ale clasei File sunt: boolean isDirectory( ) boolean isFile( ) Testeaza daca un obiect File 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. 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. Exemplu: //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();

Page 10: Laborator 3 - Manipularea datelor prin fluxuri Java

for(int i = 0; i < list.length; i++) System.out.println(list[i]); } catch(Exception e) { e.printStackTrace(); } } } Exemplu: //Utilizarea unui filtru de nume de fisiere pentru a afisa fisierle cu extensia //.class din directorul curent import java.io.*; class ClassFileFilter implements FilenameFilter { public boolean accept(File dir, String nume){ return nume.endsWith(".class"); } } public class filter_list { public static void main(String args[]){ File dir_curent = new File(); String lista_file[] = dir_curent.list(new ClassFileFilter()); for(int i = 0; i < list_file.length;i++) System.out.println(lista_file[i]); } }

Comprimare Run-Length Encoding(RLE)

Introducere Comprimarea datelor permite trimiterea sau stocarea acelorasi informatii, utilizand mai putin spatiu (mai putini octeti). In conditiile in care suntem conectati la reteaua Internet printr-o conexiune cu latimea de banda limitata, putem mari cantitea de date transferate utilizand comprimarea acestora. Unul dintre algoritmii de comprimare a datelor este denumit run-length encoding (RLE). Iata un exemplu de comprimarea folosind acest algoritm: Sa analizam secventa de date: GooooooooooolRomania Dupa cum observam, in aceasta reprezentare secventa necesita 20 de octeti pentru a putea fi memorata. Observam secventa de caractere o di cadrul datelor. In loc de a stoca fiecare caracter individual, putem reprezenta caracterele cu un singur o si suma lor, 11. Este necesar, de asemenea, un caracter special pentru a indica faptul ca secventa este comprimata. Altfel spus, putem reprezenta datele ca mai jos: G$o11Romania In acest caz, caracterul $ indica faptul ca o secventa de caractere se repetea in cadrul datelor. Aceasta noua reprezentare necesita numai 12 octeti, spre deosebire de 20 de octeti ai datelor

Page 11: Laborator 3 - Manipularea datelor prin fluxuri Java

initiale. Identificarea unor astfel de secvente de date care se repeta este esenta codificariirun-length.

Construirea unui flux filtru de iesire RLE Construim un filtru de iesire pentru a comprima datele RLE. Acest filtru se poate utiliza pentru a scrie datele comprimate intr-un fisier pe disc sau sa combinam filtru cu o operatie de soclu (socket - mai multe la disciplina Dezvoltarea de aplicatii in retele - anul III semestrul II) pentru a trimite datele comprimate in retea. Clasa RLEOutputFilter package rle; import java.io.*; import java.util.*; class RLEOutputFilter extends FilterOutputStream { int totaldate; int octeti_curent; void nwrite(int b, int count) throws IOException { if(count <= 0) //nu exista date de scris return; if((count == 1) && ((octeti_curent & 0xc0) != 0xC0)) out.write(octeti_curent); else { out.write(count |0xC0); out.write(octeti_curent); } } public RLEOutputFilter(OutputStream out){ super(out); totaldate = 0; octeti_curent = 0; } public void write(int o) throws IOException { if((o==octeti_curent) && (totaldate < 64)) totaldate++; else{ nwrite(octeti_curent,totaldate); octeti_curent = o; totaldate = 1; } } public void write(byte o[], int depls, int lung) throws IOException { for(int i = 0;i&ltlung;i++) write(o[depls+i]); }

Page 12: Laborator 3 - Manipularea datelor prin fluxuri Java

public void flush() throws IOException { nwrite(octeti_curent,totaldate); totaldate = 0; out.flush(); } }

Construirea unui flux filtru de intrare RLE Pentru a decomprima datele este necesar sa construim o clasa filtru de intrare, cu scopul de a recupera informatia initiala. Clasa RLEInputFilter package rle; import java.io.*; import java.util.*; class RLEInputFilter extends FilterInputStream { int totaldate; int octeti_curent; public int read() throws IOException { if(totaldate > 0) { totaldate--; return octeti_curent; } int o = in.read(); if(o < 0) return o; else { if((o & 0xC0) != 0xC0) return o; else{ totaldate = (o &0x3f) -1; octeti_curent = in.read(); return octeti_curent; } } } public RLEInputFilter(InputStream in){ super(in); totaldate = 0; octeti_curent = 0; } public int read(byte o[]) throws IOException {

Page 13: Laborator 3 - Manipularea datelor prin fluxuri Java

return read(o,0,o.length); } public int read(byte o[], int depls, int lung) throws IOException { int i; for(i = 0;i < lung;i++){ int c = read(); if(c== -1) break; if(o != null) o[depls+i] = (byte)c; } if(i == 0) return -1; else return 1; } } Exemplu de utilizarea a filtrului de intrare RLE: package rle; import java.io.*; import java.util.*; class Filter { public static void main(String args[]){ byte buf[] = new byte[8]; int readcount; try{ FileInputStream ins = new FileInputStream("compr.dat"); RLEInputFilter ifis = new RLEInputFilter(ins); DataInputStream dis = new DataInputStream(ifis); //citeste cate 8 octeti de date pana la sfarsitul fisierului while((readcount = dis.read(buf,0,8)) != -1) System.out.print(new String(buf,0,0,readcount)); System.out.println(); dis.close(); } catch(IOException e) { System.out.println("Eroare citire fisier : " + e); }

} } TEMA:Sa se citeasca de la tastatura un user si o parola. Acestea se vor compara cu inregistrarile existente in fiserul parole.txt.Daca se regasesc printre acestea, se va afisa mesajul "acces permis". In caz contrar se va relua procesul. Exemplu de date in fisierul parole.txt: user user gigi parolaMea user1 parola1