Stream-uri de Input/Output: Diferență între versiuni

De la WikiLabs
Jump to navigationJump to search
Fără descriere a modificării
 
(Nu s-a afișat o versiune intermediară efectuată de același utilizator)
Linia 1: Linia 1:
Un calculator comunică cu exteriorul prin una sau mai multe interfeţe de comunicare, numite şi porturi de intrare-ieşire (Input/Output - I/O). Exemple: portul serial, portul paralel, portul video, portul audio. Utilizând aceste interfeţe de comunicare, calculatorul este capabil să transmită şi să primească informaţii către/dinspre lumea exterioară prin intermediul diferitelor dispozitive periferce (monitor, imprimantă, tastatură, mouse, modem, placa de reţea, etc.)
Un calculator comunică cu exteriorul prin una sau mai multe interfețe de comunicare, numite și porturi de intrare-ieșire (Input/Output - I/O). Exemple: portul serial, portul paralel, portul video, portul audio. Utilizând aceste interfețe de comunicare, calculatorul este capabil să transmită și să primească informații către/dinspre lumea exterioară prin intermediul diferitelor dispozitive periferce (monitor, imprimantă, tastatură, mouse, modem, placă de rețea, etc.)


== Fluxuri de intrare-ieşire ==
== Fluxuri de intrare-ieșire ==


Având în vedere numărul mare de tipuri de periferice, apariţia în permanenţă a unor periferice noi, precum şi faptul că perifericele existente se află într-o continuă dezvoltare, comunicarea directă a programelor cu acestea ar fi deosebit de dificil de realizat dacă s-ar utiliza direct instrucţiunile procesorului (in/out). În plus, complexitatea comunicării cu dispozitivele periferice mai creşte şi datorită faptului că sunt necesare mai multe modalităţi de comunicare: secvenţial, aleator, binar, caracter, linie cu linie etc. Există drivere de dispozitive (device drivers) care sunt module software ce oferă o interfaţă mai simplă de comunicare cu perifericul fizic, totuşi acestea nu rezolvă complet problema compatibilităţii operaţiilor de intrare-ieşire. Ca urmare, sistemul de operare introduce la rândul lui un nivel de uniformizare a operaţiilor cu dispozitivele periferice, oferind anumite funcţii standard de comunicare cu acestea. Însă limbajele de programare (în acest caz, Java) sunt concepute să lucreze cu mai multe sisteme de operare, astfel încât sunt necesare funcţii standard (unitare) de comunicare cu dispozitivele periferice definite în cadrul limbajului de programare, independente de funcţiile oferite de sistemele de operare.
Având în vedere numărul mare de tipuri de periferice, apariția în permanență a unor periferice noi, precum și faptul că perifericele existente se află într-o continuă dezvoltare, comunicarea directă a programelor cu acestea ar fi deosebit de dificil de realizat dacă s-ar utiliza direct instrucțiunile procesorului (in/out). În plus, complexitatea comunicării cu dispozitivele periferice crește şi datorită faptului că sunt necesare mai multe modalități de comunicare: secvențial/ aleator, binar/ caracter/ linie cu linie, etc. Există drivere de dispozitive (device drivers) care sunt module software ce oferă o interfață mai simplă de comunicare cu perifericul fizic, totuși acestea nu rezolvă complet problema compatibilității operațiilor de intrare-ieșire. Ca urmare, sistemul de operare introduce la rândul lui un nivel de uniformizare a operațiilor cu dispozitivele periferice, oferind anumite funcții standard de comunicare cu acestea. Însă limbajele de programare (în acest caz, Java) sunt concepute să lucreze cu mai multe sisteme de operare, astfel încât sunt necesare funcții standard (unitare) de comunicare cu dispozitivele periferice definite în cadrul limbajului de programare, independente de funcțiile oferite de sistemele de operare.


În vederea tratării unitare a comunicaţiei, Java foloseşte conceptul de flux (stream). Apariţia conceptului de flux se datorează lui Dennis Ritchie care a implementat primul sistem de intrare-ieşire bazat pe fluxuri în cadrul sistemului de operare Unix, în anul 1984. Ideea de flux are la bază crearea unui canal de comunicatie, general valabil, între două entităţi, fie ele software sau hardware, cu condiţia ca una dintre entități să fie sursă iar cealaltă destinatie. Astfel, sursa va trimite (va scrie) informație în canalul de comunicație (flux) iar destinația va prelua (va citi) informația din canal. Astfel conceptul de flux oferă posibilitatea tratarii unitare a oricarui tip de comunicaţie, cu orice tip de entitate din sistem. Definirea unui flux (a unui canal de comunicație) se face prin definirea capetelor lui, a sursei şi a destinației. Datorită faptului că există două direcții de comunicare se poate face o delimitare în două tipuri de fluxuri: flux de intrare (input stream) şi flux de ieşire (output stream). Fluxurile de intrare sunt fluxuri care se conectează cu dispozitivele care furnizează date (exemplu, tastatura) iar fluxurile de ieşire se conectează cu dispozitive care preiau date (exemplu: monitorul). Sursa sau destinaţia pot fi atat periferice cât şi module software.
În vederea tratării unitare a comunicației, Java folosește conceptul de flux (stream). Apariția conceptului de flux se datorează lui [http://en.wikipedia.org/wiki/Dennis_Ritchie Dennis Ritchie] care a implementat primul sistem de intrare-ieșire bazat pe fluxuri în cadrul sistemului de operare Unix, în anul 1984. Ideea de flux are la bază crearea unui canal de comunicație, general valabil, între două entități, fie ele software sau hardware, cu condiția ca una dintre entități să fie sursă, iar cealaltă destinație. Astfel, sursa va trimite (va scrie) informație în canalul de comunicație (flux), iar destinația va prelua (va citi) informația din canal. Astfel, conceptul de flux oferă posibilitatea tratării unitare a oricărui tip de comunicație, cu orice tip de entitate din sistem. Definirea unui flux se face prin definirea capetelor lui (a sursei și a destinației). Datorită faptului că există două direcții de comunicare, se poate face o separare în două tipuri de fluxuri: flux de intrare (input stream) și flux de ieșire (output stream). Fluxurile de intrare sunt fluxuri care se conectează cu dispozitivele care furnizează date (ex: tastatura) iar fluxurile de ieșire se conectează cu dispozitive care preiau date (ex: monitorul). Sursa sau destinația pot fi atat periferice cât și module software.


== Stream-uri pentru consolă ==
== Stream-uri pentru consolă ==


Limbajul Java pune la dispoziția utilizatorului trei fluxuri standard pentru comunicare cu consola:
Limbajul Java pune la dispoziția utilizatorului trei fluxuri standard pentru comunicare cu consola:
* fluxul standard de intrare (Standard Input) – folosit pentru citirea datelor, de tip InputStream.
* fluxul standard de intrare (Standard Input) – de tip InputStream, folosit pentru citirea datelor;
* fluxul standard de ieşire (Standard Output) – folosit pentru afişarea datelor, de tip PrintStream.
* fluxul standard de ieșire (Standard Output) – de tip PrintStream, folosit pentru afișarea datelor;
* fluxul standard de eroare (Standard Error) – folosit pentru afişarea erorilor, de tip PrintStream.
* fluxul standard de eroare (Standard Error) – de tip PrintStream, folosit pentru afișarea erorilor.


În Java, toate fluxurile standard sunt accesate prin clasa '''java.lang.System'''. Astfel, pentru Standard Input există fluxul '''System.in''', pentru Standard Output, '''System.out''' iar pentru Standard Error, '''System.err'''. Cei trei membri '''in''', '''out''', '''err''' ai clasei '''System''' sunt membri statici.
În Java, toate fluxurile standard sunt accesate prin clasa '''java.lang.System'''. Astfel, pentru Standard Input există fluxul '''System.in''', pentru Standard Output, '''System.out''' iar pentru Standard Error, '''System.err'''. Cei trei membri '''in''', '''out''', '''err''' ai clasei '''System''' sunt statici.


Clasele care descriu operațiile de intrare-ieșire se află în pachetul '''java.io'''. Acesta conține două categorii de fluxuri: fluxuri la nivel de octet și fluxuri la nivel de caracter. Fluxurile la nivel de octet au fost introduse începând cu versiunea JDK 1.0.2, iar fluxuruile la nivel de caracter au fost introduse începând cu versiunea JDK 1.1. Acestea din urmă nu înlocuiesc, ci completează funcționarea fluxurilor cu posibilitatea de introducere și extragere a caracterelor (care în Java au 2 octeți).
Clasele care descriu operațiile de intrare-ieșire se află în pachetul '''java.io'''. Acesta conține două categorii de fluxuri: fluxuri la nivel de octet și fluxuri la nivel de caracter. Fluxurile la nivel de octet au fost introduse începând cu versiunea JDK 1.0.2, iar fluxuruile la nivel de caracter au fost introduse începând cu versiunea JDK 1.1. Acestea din urmă nu înlocuiesc, ci completează funcționarea fluxurilor, cu posibilitatea de introducere și extragere a caracterelor (care în Java au 2 octeți).


== Fluxuri de intrare-ieșire la nivel de octet ==
== Fluxuri de intrare-ieșire la nivel de octet ==


Fluxurile la nivel de octet (ca și fluxurile la nivel de caracter) se împart în fluxuri de intrare și fluxuri de ieşire.
Fluxurile la nivel de octet (ca și fluxurile la nivel de caracter) se împart în fluxuri de intrare și fluxuri de ieșire.


=== Fluxuri de intrare la nivel de octet ===
=== Fluxuri de intrare la nivel de octet ===
Linia 44: Linia 44:
Funcția ''read()'', fără nici un parametru, citește octetul curent din flux și îl returnează sub forma unui întreg cu valori între 0 şi 255. Dacă s-a ajuns la capătul fluxului, se returnează valoarea –1.  
Funcția ''read()'', fără nici un parametru, citește octetul curent din flux și îl returnează sub forma unui întreg cu valori între 0 şi 255. Dacă s-a ajuns la capătul fluxului, se returnează valoarea –1.  


Funcțiile ''read()'', având ca parametru un tablou de octeți, citesc de la poziția curentă din flux un număr de octeți egal cu ''len'' sau cu lungimea tabloului ''b'', și îl scriu în vectorul ''b'', la poziția ''off'', dacă aceasta este specificată. Ele returnează numărul de octeţi citiți în buffer-ul ''b'' sau –1 daca s-a ajuns la capătul fluxului. Funcţia ''skip()'' este utilizată pentru a muta poziția cursorului de citire peste un anumit număr de octeți specificat prin parametrul ''n''.  
Funcțiile ''read()'', având ca parametru un tablou de octeți, citesc de la poziția curentă din flux un număr de octeți egal cu ''len'' sau cu lungimea tabloului ''b'' și îl scriu în vectorul ''b'', la poziția ''off'', dacă aceasta este specificată. Ele returnează numărul de octeţi citiți în buffer-ul ''b'' sau –1 dacă s-a ajuns la capătul fluxului.  
 
Funcţia ''skip()'' este utilizată pentru a muta poziția cursorului de citire peste un anumit număr de octeți specificat prin parametrul ''n''.  


Funcțiile ''mark()'' și ''reset()'' se folosesc împreună, oferind posibilitatea reluării citirii din flux. Prin funcția ''mark()'' se memorează poziția curentă în flux, specificând prin argumentul ''readlimit'' maximul de octeți (se crează un buffer de dimensiune ''readlimit'') care se pot citi până la apelul funcției ''reset()''. Prin funcția ''reset()'' se repoziționează cursorul de citire din flux la poziția la care s-a făcut marcarea prin funcția ''mark()''. Aceste două funcții sunt valabile doar în cazul în care fluxul suportă marcarea. Verificarea acestui lucru se face prin apelul funcției ''markSupported()''.  
Funcțiile ''mark()'' și ''reset()'' se folosesc împreună, oferind posibilitatea reluării citirii din flux. Prin funcția ''mark()'' se memorează poziția curentă în flux, specificând prin argumentul ''readlimit'' maximul de octeți (se crează un buffer de dimensiune ''readlimit'') care se pot citi până la apelul funcției ''reset()''. Prin funcția ''reset()'' se repoziționează cursorul de citire din flux la poziția la care s-a făcut marcarea prin funcția ''mark()''. Aceste două funcții sunt valabile doar în cazul în care fluxul suportă marcarea. Verificarea acestui lucru se face prin apelul funcției ''markSupported()''.  
Linia 53: Linia 55:


Pentru a utiliza clasa '''InputStream''' în aplicații, a fost nevoie de construirea unor clase derivate din aceasta, clase care să fie conectate cu surse de date reale, existente în sistem. Din categoria claselor pentru fluxuri de intrare la nivel de octet conectate la diferite tipuri de resurse fac parte:
Pentru a utiliza clasa '''InputStream''' în aplicații, a fost nevoie de construirea unor clase derivate din aceasta, clase care să fie conectate cu surse de date reale, existente în sistem. Din categoria claselor pentru fluxuri de intrare la nivel de octet conectate la diferite tipuri de resurse fac parte:
* [http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html ByteArrayInputStream] permite conectarea unui flux la un tablou de octeţi;
* [http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html ByteArrayInputStream] permite conectarea unui flux la un tablou de octeți;
* [http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html FileInputStream] oferă posibilitatea conectării cu un fişier pentru a citi datele înregistrate în acesta;
* [http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html FileInputStream] oferă posibilitatea conectării cu un fișier pentru a citi datele înregistrate în acesta;
* [http://docs.oracle.com/javase/7/docs/api/java/io/SequenceInputStream.html SequenceInputStream] permite combinarea mai multor fluxuri având ca rezultat obținerea unui singur flux;
* [http://docs.oracle.com/javase/7/docs/api/java/io/SequenceInputStream.html SequenceInputStream] permite combinarea mai multor fluxuri având ca rezultat obținerea unui singur flux;
* [http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioInputStream.html AudioInputStream] permite conectarea cu un stream audio.
* [http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioInputStream.html AudioInputStream] permite conectarea cu un stream audio.




O altă categorie importantă de clase derivate din '''InputStream''', de data aceasta însă nu direct, sunt clasele de tip filtru derivate din clasa [http://docs.oracle.com/javase/7/docs/api/java/io/FilterInputStream.html FilterInputStream] care este la randul ei derivată din clasa '''InputStream'''. Aceste tipuri de clase nu oferă nici o alta sursă pentru fluxuri față de clasele amintite anterior. Ele lucrează ca o înfăşurătoare a claselor care se conectează direct la sursă, oferind acestora din urmă funcții (proprietăţi) suplimentare de citire din flux (exemplu: citire pe linie, citire numere, etc). Ca exemple de astfel de clase amintim:
O altă categorie importantă de clase derivate din '''InputStream''', de data aceasta însă nu direct, sunt clasele de tip filtru derivate din clasa [http://docs.oracle.com/javase/7/docs/api/java/io/FilterInputStream.html FilterInputStream] care este la rândul ei derivată din clasa '''InputStream'''. Aceste tipuri de clase nu oferă nici o alta sursă pentru fluxuri față de clasele amintite anterior. Ele lucrează ca o înfășurătoare a claselor care se conectează direct la sursă, oferind acestora din urmă funcții (proprietăți) suplimentare de citire din flux (exemplu: citire pe linie, citire numere, etc). Ca exemple de astfel de clase amintim:
* [http://docs.oracle.com/javase/7/docs/api/java/io/DataInputStream.html DataInputStream] este una din cele mai utilizate clase dintre cele de tip filtru. Conține mai multe funcții care permit citirea unor tipuri fundamentale de date ('''int''', '''float''', etc) într-un mod independent de sistem și de mașină (procesor);
* [http://docs.oracle.com/javase/7/docs/api/java/io/DataInputStream.html DataInputStream] este una din cele mai utilizate clase dintre cele de tip filtru. Conține mai multe funcții care permit citirea unor tipuri fundamentale de date ('''int''', '''float''', etc) într-un mod independent de sistem și de mașină (procesor);
* [http://docs.oracle.com/javase/7/docs/api/java/io/PushbackInputStream.html PushbackInputStream] oferă posibilităţi de revenire, permiţând citirea din nou a datelor deja citite.
* [http://docs.oracle.com/javase/7/docs/api/java/io/PushbackInputStream.html PushbackInputStream] oferă posibilități de revenire, permițând citirea din nou a datelor deja citite.


În continuare este prezentat un exemplu de utilizare a clasei '''ByteArrayInputStream''':
În continuare este prezentat un exemplu de utilizare a clasei '''ByteArrayInputStream''':
Linia 78: Linia 80:
</syntaxhighlight>
</syntaxhighlight>


Prin intermediul clasei '''ByteArrayInputStream''' se pot citi datele din tablou utilizând operațiile de citire din flux, fără a mai fi nevoie de a contoriza poziția de citire din tablou. Conectarea se face simplu prin transmiterea tabloului respectiv ca şi parametru în constructorul fluxului. Exemplul construieşte un tablou de 10 octeţi inițializându-le cu valori de la 0 la 9, după care se citesc aceste valori din tablou prin intermediul fluxului creat. Se observă că citirea din flux se face până în momentul în care funcția ''read()'' returnează –1, semnalizând că s-a ajuns la sfârșitul fluxului, deci la sfârşitul tabloului.
Prin intermediul clasei '''ByteArrayInputStream''' se pot citi datele din tablou utilizând operațiile de citire din flux, fără a mai fi nevoie de a contoriza poziția de citire din tablou. Conectarea se face simplu prin transmiterea tabloului respectiv ca și parametru în constructorul fluxului. Exemplul construiește un tablou de 10 octeți, inițializându-l cu valori de la 0 la 9, după care se citesc aceste valori din tablou prin intermediul fluxului creat. Se observă că citirea din flux se face până în momentul în care funcția ''read()'' returnează –1, semnalizând că s-a ajuns la sfârșitul fluxului, deci la sfârşitul tabloului.


=== Fluxuri de ieşire orientate pe octet ===
=== Fluxuri de ieșire orientate pe octet ===


Pentru scrierea datelor folosind conceptul de flux orientat pe octet, se foloseşte o ierarhie de clase derivată din clasa '''OutputStream'''. Ca şi clasa '''InputStream''', clasa [http://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html OutputStream] este o clasa abstractă, oferind o interfaţă de declarare a unor metode de scriere general valabile. Clasa are următoarea definiţie:
[[Fișier:outputstream.png|Reprezentare grafică a unui OutputStream generic și a unui FileOutputStream]]
 
Pentru scrierea datelor folosind conceptul de flux orientat pe octet, se folosește o ierarhie de clase derivată din clasa '''OutputStream'''. Ca și clasa '''InputStream''', clasa [http://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html OutputStream] este o clasa abstractă, oferind o interfață de declarare a unor metode de scriere general valabile. Clasa are următoarea definiție:
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
public abstract class OutputStream{
public abstract class OutputStream{
Linia 93: Linia 97:
</syntaxhighlight>
</syntaxhighlight>


Funcţia ''write()'' scrie în flux octetul cel mai puţin semnficativ al argumentului, care este de tip întreg. Celelalte funcţii permit scrierea în flux a unui tablou de octeţi sau a unei secţiuni din tabloul de octeţi b începand de la pozitia off şi avand lungimea len.  
Funcţia ''write()'' scrie în flux octetul cel mai puțin semnficativ al argumentului, care este de tip '''int'''. Celelalte funcții permit scrierea în flux a unui tablou de octeți sau a unei secțiuni din tabloul de octeți ''b'' începând de la poziția ''off'' și având lungimea ''len''.  


Funcţia ''flush()'' forţează transmiterea datelor la destinatie (capătul flux-ului) şi este utilă în cazul în care se foloseşte un buffer de scriere (goleşte buffer-ul).  
Funcția ''flush()'' forțează scriea datelor în stream și este utilă în cazul în care se folosește un buffer de scriere (golește buffer-ul).  


Funcţia ''close()'' închide fluxul deschis prin operaţia de creare a fluxului, eliberând astfel orice resursă sistem ocupată de fluxul respectiv.
Funcția ''close()'' închide fluxul deschis prin operația de creare a fluxului, eliberând astfel orice resursă de sistem ocupată de fluxul respectiv.


Ca şi în cazul clasei ''InputStream'', clasele derivate din ''OutputStream'' pot fi delimitate în două categorii: fluxuri propriu-zise ([http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayOutputStream.html ByteArrayOutputStream], [http://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html FileOutputStream], etc) care se conectează la o destinaţie şi fluxuri de tip filtru (derivate din [http://docs.oracle.com/javase/7/docs/api/java/io/FilterOutputStream.html FilterOutputStream]). Ultimele tipuri de fluxuri se conectează la primele, îmbogăţind funcţionarea acestora.
Ca și în cazul clasei ''InputStream'', clasele derivate din ''OutputStream'' pot fi separate în două categorii: fluxuri propriu-zise ([http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayOutputStream.html ByteArrayOutputStream], [http://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html FileOutputStream], etc) care se conectează la o destinație, și fluxuri de tip filtru (derivate din [http://docs.oracle.com/javase/7/docs/api/java/io/FilterOutputStream.html FilterOutputStream]). Ultimele tipuri de fluxuri se conectează la primele, îmbogățind funcționarea acestora.


Fluxurile standard de ieşire ('''System.out''') şi de eroare ('''System.err''') sunt membri statici ai clasei '''System''' de tipul [http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html PrintStream], care extinde clasa '''FilterOutputStream'''. Acestea sunt conectate cu display-ul şi se folosesc prin convenţie în două situatii distincte: primul pentru afisarea mesajelor către utilizatorul programului şi al doilea pentru afisarea mesajelor de eroare (de cele mai multe ori în scop de depanare).
Fluxurile standard de ieșire ('''System.out''') și de eroare ('''System.err''') sunt membri statici ai clasei '''System''' de tipul [http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html PrintStream], care extinde clasa '''FilterOutputStream'''. Acestea sunt conectate cu display-ul și se folosesc prin convenție în două situații distincte: primul pentru afișarea mesajelor către utilizatorul programului, şi al doilea pentru afișarea mesajelor de eroare (de cele mai multe ori în scop de depanare).


În continuare este prezentat un exemplu de utilizare a clasei '''ByteArrayOutputStream''', prin care se afişează toate caracterele pe 8 biţi:
În continuare este prezentat un exemplu de utilizare a clasei '''ByteArrayOutputStream''', prin care se afișează toate valorile pe 8 biţi:
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
ByteArrayOutputStream _buffer = new ByteArrayOutputStream(256);
ByteArrayOutputStream _buffer = new ByteArrayOutputStream(256);
Linia 117: Linia 121:
</syntaxhighlight>
</syntaxhighlight>


Clasa '''ByteArrayOutputStream''' este clasa corespondentă clasei '''ByteArrayInputStream''' şi permite conectarea unui flux la un buffer (un tablou de octeţi). Buffer-ul se crează în momentul în care un obiect de tipul '''ByteArrayOutputStream''' se instanţiază folosind unul dintre cei doi constructori. Această clasă oferă o metodă de stocare a datelor în memorie înainte de a le trimite în altă parte. Pentru transmiterea datelor se foloseste metoda ''writeTo()''.
Clasa '''ByteArrayOutputStream''' este clasa corespondentă clasei '''ByteArrayInputStream''' și permite conectarea unui flux la un buffer (un tablou de octeți). Buffer-ul se crează în momentul în care un obiect de tipul '''ByteArrayOutputStream''' se instanțiază folosind unul dintre cei doi constructori. Această clasă oferă o metodă de stocare a datelor în memorie înainte de a le trimite mai departe. Pentru transmiterea datelor se folosește metoda ''writeTo()''. Această funcție scrie conținutul buffer-ului în fluxul ''out'' transmis ca parametru. Spre deosebire de clasa '''ByteArrayInputStream''' unde tabloul (buffer-ul) era transmis prin constructorul clasei, în cazul clasei '''ByteArrayOutputStream''', pentru a avea o referintă la acest buffer, este nevoie să se apeleze funcția ''toByteArray()''. Această funcție creează un tablou de dimensiune egală cu numărul de octeți înscriși în flux și care conține acești octeți.
 
Această funcţie scrie continutul buffer-ului în fluxul ''out'' transmis ca şi parametru. Spre deosebire de clasa '''ByteArrayInputStream''' unde tabloul (buffer-ul) era transmis prin constructorul clasei, în cazul clasei '''ByteArrayOutputStream''', pentru a avea o referintă la acest buffer, este nevoie să se apeleze funcţia ''toByteArray()''. Aceasta funcţie crează un tablou de dimensiune egală cu numarul de octeţi înscriși în flux şi care conține aceşti octeţi.


== Fluxuri de intrare-ieşire la nivel de caracter ==
== Fluxuri de intrare-ieșire la nivel de caracter ==


Clasele pentru fluxurile la nivel de caracter au la bază clasa '''Reader''' pentru citirea datelor şi clasa '''Writer''' pentru scrierea datelor. Majoritatea acestor clase se suprapun peste clasele pentru fluxurile la nivel de octet, oferind în principal aceleaşi funcționalități. Deosebirea între clasele de tip '''InputStream/OutputStream''' şi cele de tip '''Reader/Writer''' este aceea ca primele clase lucrează cu fluxuri de octeţi (8 biţi) în timp ce ultimele lucrează cu fluxuri de caractere reprezentate pe 16 biţi în format [http://en.wikipedia.org/wiki/Unicode Unicode] (standard international de codare a caracterelor care este capabil să reprezinte caracterele tuturor limbilor scrie).
Clasele pentru fluxurile la nivel de caracter au la bază clasa '''Reader''' pentru citirea datelor și clasa '''Writer''' pentru scrierea datelor. Majoritatea acestor clase se suprapun peste clasele pentru fluxurile la nivel de octet, oferind în principal aceleași funcționalități. Deosebirea între clasele de tip '''InputStream/OutputStream''' și cele de tip '''Reader/Writer''' este aceea primele clase lucrează cu fluxuri de octeți (8 biți), în timp ce ultimele lucrează cu fluxuri de caractere reprezentate pe 16 biți în format [http://en.wikipedia.org/wiki/Unicode Unicode] (standard internațional de codare a caracterelor care este capabil să reprezinte caracterele tuturor limbilor scrie).


Cu toate că noile clase '''Reader/Writer''' dublează clasele '''InputStream/OutputStream''', acestea din urmă nu au fost abandonate, datorită în principal cerinţei de compatibilitate cu aplicaţiile dezvoltate într-o versiune mai veche de Java. Totuşi există unele metode din vechile clase care se recomanda să nu fie folosite, fiind caracterizare ca depăşite (deprecated). Funcţiile oferite de clasele '''InputStream/OutputStream''' se regăsesc în clasele '''Reader/Writer''' cu mici deosebiri, acest lucru reflectându-se şi în denumirile asemănătoare ale celor două tipuri de clase de tip flux, prefixele fiind aceleaşi.
Cu toate că noile clase '''Reader/Writer''' dublează clasele '''InputStream/OutputStream''', acestea din urmă nu au fost abandonate, datorită în principal cerinței de compatibilitate cu aplicațiile dezvoltate într-o versiune mai veche de Java. Totuși, există unele metode din vechile clase care se recomandă a nu a mai fi folosite, fiind caracterizare ca depășite (''deprecated''). Funcțiile oferite de clasele '''InputStream/OutputStream''' se regăsesc în clasele '''Reader/Writer''' cu mici deosebiri, acest lucru reflectându-se și în denumirile asemănătoare ale celor două tipuri de clase de tip flux, prefixele fiind aceleași.
În cazul claselor de citire a datelor se pot observa următoarele corespondenţe:
În cazul claselor de citire a datelor se pot observa următoarele corespondențe:
* '''InputStream''' - [http://docs.oracle.com/javase/7/docs/api/java/io/Reader.html Reader]
* '''InputStream''' - [http://docs.oracle.com/javase/7/docs/api/java/io/Reader.html Reader]
* '''ByteArrayInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/CharArrayReader.html CharArrayReader]
* '''ByteArrayInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/CharArrayReader.html CharArrayReader]
* '''StringBufferInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/StringReader.html StringReader]
* '''StringBufferInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/StringReader.html StringReader]
* '''FileInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/FileReader.html FileReader]
* '''FileInputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/FileReader.html FileReader]
* Există unele clase, '''SequenceInputStream''', şi '''DataInputStream''' care nu au o versiune de tip '''Reader'''.
* Există unele clase, '''SequenceInputStream''', și '''DataInputStream''' care nu au o versiune de tip '''Reader'''.


În cazul claselor pentru scriere se pot observa următoarele corespondentele:
În cazul claselor pentru scriere, se pot observa următoarele corespondențe:
* '''OutputStream''' - [http://docs.oracle.com/javase/7/docs/api/java/io/Writer.html Writer]
* '''OutputStream''' - [http://docs.oracle.com/javase/7/docs/api/java/io/Writer.html Writer]
* '''ByteArrayOutputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/CharArrayWriter.html CharArrayWriter]
* '''ByteArrayOutputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/CharArrayWriter.html CharArrayWriter]
* '''FileOutputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/FileWriter.html FileWriter]
* '''FileOutputStream''' – [http://docs.oracle.com/javase/7/docs/api/java/io/FileWriter.html FileWriter]

Versiunea curentă din 23 august 2012 07:07

Un calculator comunică cu exteriorul prin una sau mai multe interfețe de comunicare, numite și porturi de intrare-ieșire (Input/Output - I/O). Exemple: portul serial, portul paralel, portul video, portul audio. Utilizând aceste interfețe de comunicare, calculatorul este capabil să transmită și să primească informații către/dinspre lumea exterioară prin intermediul diferitelor dispozitive periferce (monitor, imprimantă, tastatură, mouse, modem, placă de rețea, etc.)

Fluxuri de intrare-ieșire

Având în vedere numărul mare de tipuri de periferice, apariția în permanență a unor periferice noi, precum și faptul că perifericele existente se află într-o continuă dezvoltare, comunicarea directă a programelor cu acestea ar fi deosebit de dificil de realizat dacă s-ar utiliza direct instrucțiunile procesorului (in/out). În plus, complexitatea comunicării cu dispozitivele periferice crește şi datorită faptului că sunt necesare mai multe modalități de comunicare: secvențial/ aleator, binar/ caracter/ linie cu linie, etc. Există drivere de dispozitive (device drivers) care sunt module software ce oferă o interfață mai simplă de comunicare cu perifericul fizic, totuși acestea nu rezolvă complet problema compatibilității operațiilor de intrare-ieșire. Ca urmare, sistemul de operare introduce la rândul lui un nivel de uniformizare a operațiilor cu dispozitivele periferice, oferind anumite funcții standard de comunicare cu acestea. Însă limbajele de programare (în acest caz, Java) sunt concepute să lucreze cu mai multe sisteme de operare, astfel încât sunt necesare funcții standard (unitare) de comunicare cu dispozitivele periferice definite în cadrul limbajului de programare, independente de funcțiile oferite de sistemele de operare.

În vederea tratării unitare a comunicației, Java folosește conceptul de flux (stream). Apariția conceptului de flux se datorează lui Dennis Ritchie care a implementat primul sistem de intrare-ieșire bazat pe fluxuri în cadrul sistemului de operare Unix, în anul 1984. Ideea de flux are la bază crearea unui canal de comunicație, general valabil, între două entități, fie ele software sau hardware, cu condiția ca una dintre entități să fie sursă, iar cealaltă destinație. Astfel, sursa va trimite (va scrie) informație în canalul de comunicație (flux), iar destinația va prelua (va citi) informația din canal. Astfel, conceptul de flux oferă posibilitatea tratării unitare a oricărui tip de comunicație, cu orice tip de entitate din sistem. Definirea unui flux se face prin definirea capetelor lui (a sursei și a destinației). Datorită faptului că există două direcții de comunicare, se poate face o separare în două tipuri de fluxuri: flux de intrare (input stream) și flux de ieșire (output stream). Fluxurile de intrare sunt fluxuri care se conectează cu dispozitivele care furnizează date (ex: tastatura) iar fluxurile de ieșire se conectează cu dispozitive care preiau date (ex: monitorul). Sursa sau destinația pot fi atat periferice cât și module software.

Stream-uri pentru consolă

Limbajul Java pune la dispoziția utilizatorului trei fluxuri standard pentru comunicare cu consola:

  • fluxul standard de intrare (Standard Input) – de tip InputStream, folosit pentru citirea datelor;
  • fluxul standard de ieșire (Standard Output) – de tip PrintStream, folosit pentru afișarea datelor;
  • fluxul standard de eroare (Standard Error) – de tip PrintStream, folosit pentru afișarea erorilor.

În Java, toate fluxurile standard sunt accesate prin clasa java.lang.System. Astfel, pentru Standard Input există fluxul System.in, pentru Standard Output, System.out iar pentru Standard Error, System.err. Cei trei membri in, out, err ai clasei System sunt statici.

Clasele care descriu operațiile de intrare-ieșire se află în pachetul java.io. Acesta conține două categorii de fluxuri: fluxuri la nivel de octet și fluxuri la nivel de caracter. Fluxurile la nivel de octet au fost introduse începând cu versiunea JDK 1.0.2, iar fluxuruile la nivel de caracter au fost introduse începând cu versiunea JDK 1.1. Acestea din urmă nu înlocuiesc, ci completează funcționarea fluxurilor, cu posibilitatea de introducere și extragere a caracterelor (care în Java au 2 octeți).

Fluxuri de intrare-ieșire la nivel de octet

Fluxurile la nivel de octet (ca și fluxurile la nivel de caracter) se împart în fluxuri de intrare și fluxuri de ieșire.

Fluxuri de intrare la nivel de octet

Reprezentare grafică a unui InputStream generic și a unui FileInputStream

Clasele pentru fluxurile la nivel de octet au ca rădăcină clasa InputStream, o clasa abstractă care oferă funcțiile de citire general valabile. Definiția clasei InputStream este:

public abstract class InputStream{
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException;
    public int read(byte b[], int off, int len) throws IOException;
    public long skip(long n) throws IOException;
    public int available() throws IOException;
    public void close() throws IOException;
    public synchronized void mark(int readlimit);
    public synchronized void reset() throws IOException;
    public boolean markSupported();
}

Funcția read(), fără nici un parametru, citește octetul curent din flux și îl returnează sub forma unui întreg cu valori între 0 şi 255. Dacă s-a ajuns la capătul fluxului, se returnează valoarea –1.

Funcțiile read(), având ca parametru un tablou de octeți, citesc de la poziția curentă din flux un număr de octeți egal cu len sau cu lungimea tabloului b și îl scriu în vectorul b, la poziția off, dacă aceasta este specificată. Ele returnează numărul de octeţi citiți în buffer-ul b sau –1 dacă s-a ajuns la capătul fluxului.

Funcţia skip() este utilizată pentru a muta poziția cursorului de citire peste un anumit număr de octeți specificat prin parametrul n.

Funcțiile mark() și reset() se folosesc împreună, oferind posibilitatea reluării citirii din flux. Prin funcția mark() se memorează poziția curentă în flux, specificând prin argumentul readlimit maximul de octeți (se crează un buffer de dimensiune readlimit) care se pot citi până la apelul funcției reset(). Prin funcția reset() se repoziționează cursorul de citire din flux la poziția la care s-a făcut marcarea prin funcția mark(). Aceste două funcții sunt valabile doar în cazul în care fluxul suportă marcarea. Verificarea acestui lucru se face prin apelul funcției markSupported().

Funcţia available() returnează numărul de octeți disponibili pentru citire din flux.

Funcția close() este funcția care închide un flux. Odată terminat lucrul cu un flux, acesta trebuie închis, eliberând astfel resursele de sistem ocupate de acel flux. În mod normal JVM închide aceste fluxuri în momentul în care programul se termină.

Pentru a utiliza clasa InputStream în aplicații, a fost nevoie de construirea unor clase derivate din aceasta, clase care să fie conectate cu surse de date reale, existente în sistem. Din categoria claselor pentru fluxuri de intrare la nivel de octet conectate la diferite tipuri de resurse fac parte:


O altă categorie importantă de clase derivate din InputStream, de data aceasta însă nu direct, sunt clasele de tip filtru derivate din clasa FilterInputStream care este la rândul ei derivată din clasa InputStream. Aceste tipuri de clase nu oferă nici o alta sursă pentru fluxuri față de clasele amintite anterior. Ele lucrează ca o înfășurătoare a claselor care se conectează direct la sursă, oferind acestora din urmă funcții (proprietăți) suplimentare de citire din flux (exemplu: citire pe linie, citire numere, etc). Ca exemple de astfel de clase amintim:

  • DataInputStream este una din cele mai utilizate clase dintre cele de tip filtru. Conține mai multe funcții care permit citirea unor tipuri fundamentale de date (int, float, etc) într-un mod independent de sistem și de mașină (procesor);
  • PushbackInputStream oferă posibilități de revenire, permițând citirea din nou a datelor deja citite.

În continuare este prezentat un exemplu de utilizare a clasei ByteArrayInputStream:

byte _buffer[] = new byte[10];
for(byte i = 0; i < _buffer.length; i++){
    _buffer[i] = i;
}

ByteArrayInputStream _bufferStream = new ByteArrayInputStream(_buffer);
int _byteRead;
while((_byteRead = _bufferStream.read()) != -1){
    System.out.print(  + _byteRead);
}
System.out.println();

Prin intermediul clasei ByteArrayInputStream se pot citi datele din tablou utilizând operațiile de citire din flux, fără a mai fi nevoie de a contoriza poziția de citire din tablou. Conectarea se face simplu prin transmiterea tabloului respectiv ca și parametru în constructorul fluxului. Exemplul construiește un tablou de 10 octeți, inițializându-l cu valori de la 0 la 9, după care se citesc aceste valori din tablou prin intermediul fluxului creat. Se observă că citirea din flux se face până în momentul în care funcția read() returnează –1, semnalizând că s-a ajuns la sfârșitul fluxului, deci la sfârşitul tabloului.

Fluxuri de ieșire orientate pe octet

Reprezentare grafică a unui OutputStream generic și a unui FileOutputStream

Pentru scrierea datelor folosind conceptul de flux orientat pe octet, se folosește o ierarhie de clase derivată din clasa OutputStream. Ca și clasa InputStream, clasa OutputStream este o clasa abstractă, oferind o interfață de declarare a unor metode de scriere general valabile. Clasa are următoarea definiție:

public abstract class OutputStream{
    public abstract void write(int i) throws IOException;
    public void write (byte b[]) throws IOException;
    public void write (byte b[], int off, int len) throws IOException;
    public long flush() throws IOException;
    public void close() throws IOException;
}

Funcţia write() scrie în flux octetul cel mai puțin semnficativ al argumentului, care este de tip int. Celelalte funcții permit scrierea în flux a unui tablou de octeți sau a unei secțiuni din tabloul de octeți b începând de la poziția off și având lungimea len.

Funcția flush() forțează scriea datelor în stream și este utilă în cazul în care se folosește un buffer de scriere (golește buffer-ul).

Funcția close() închide fluxul deschis prin operația de creare a fluxului, eliberând astfel orice resursă de sistem ocupată de fluxul respectiv.

Ca și în cazul clasei InputStream, clasele derivate din OutputStream pot fi separate în două categorii: fluxuri propriu-zise (ByteArrayOutputStream, FileOutputStream, etc) care se conectează la o destinație, și fluxuri de tip filtru (derivate din FilterOutputStream). Ultimele tipuri de fluxuri se conectează la primele, îmbogățind funcționarea acestora.

Fluxurile standard de ieșire (System.out) și de eroare (System.err) sunt membri statici ai clasei System de tipul PrintStream, care extinde clasa FilterOutputStream. Acestea sunt conectate cu display-ul și se folosesc prin convenție în două situații distincte: primul pentru afișarea mesajelor către utilizatorul programului, şi al doilea pentru afișarea mesajelor de eroare (de cele mai multe ori în scop de depanare).

În continuare este prezentat un exemplu de utilizare a clasei ByteArrayOutputStream, prin care se afișează toate valorile pe 8 biţi:

ByteArrayOutputStream _buffer = new ByteArrayOutputStream(256);
try{
    for(int i=0;i<256;i++) {
        _buffer.write(i);
    }
    _buffer.writeTo(System.out);
    byte[] _tablou = _buffer.toByteArray();
}catch(IOException _e){
    System.out.println("Eroare "+ _e.getMessage());
}

Clasa ByteArrayOutputStream este clasa corespondentă clasei ByteArrayInputStream și permite conectarea unui flux la un buffer (un tablou de octeți). Buffer-ul se crează în momentul în care un obiect de tipul ByteArrayOutputStream se instanțiază folosind unul dintre cei doi constructori. Această clasă oferă o metodă de stocare a datelor în memorie înainte de a le trimite mai departe. Pentru transmiterea datelor se folosește metoda writeTo(). Această funcție scrie conținutul buffer-ului în fluxul out transmis ca parametru. Spre deosebire de clasa ByteArrayInputStream unde tabloul (buffer-ul) era transmis prin constructorul clasei, în cazul clasei ByteArrayOutputStream, pentru a avea o referintă la acest buffer, este nevoie să se apeleze funcția toByteArray(). Această funcție creează un tablou de dimensiune egală cu numărul de octeți înscriși în flux și care conține acești octeți.

Fluxuri de intrare-ieșire la nivel de caracter

Clasele pentru fluxurile la nivel de caracter au la bază clasa Reader pentru citirea datelor și clasa Writer pentru scrierea datelor. Majoritatea acestor clase se suprapun peste clasele pentru fluxurile la nivel de octet, oferind în principal aceleași funcționalități. Deosebirea între clasele de tip InputStream/OutputStream și cele de tip Reader/Writer este aceea că primele clase lucrează cu fluxuri de octeți (8 biți), în timp ce ultimele lucrează cu fluxuri de caractere reprezentate pe 16 biți în format Unicode (standard internațional de codare a caracterelor care este capabil să reprezinte caracterele tuturor limbilor scrie).

Cu toate că noile clase Reader/Writer dublează clasele InputStream/OutputStream, acestea din urmă nu au fost abandonate, datorită în principal cerinței de compatibilitate cu aplicațiile dezvoltate într-o versiune mai veche de Java. Totuși, există unele metode din vechile clase care se recomandă a nu a mai fi folosite, fiind caracterizare ca depășite (deprecated). Funcțiile oferite de clasele InputStream/OutputStream se regăsesc în clasele Reader/Writer cu mici deosebiri, acest lucru reflectându-se și în denumirile asemănătoare ale celor două tipuri de clase de tip flux, prefixele fiind aceleași. În cazul claselor de citire a datelor se pot observa următoarele corespondențe:

  • InputStream - Reader
  • ByteArrayInputStreamCharArrayReader
  • StringBufferInputStreamStringReader
  • FileInputStreamFileReader
  • Există unele clase, SequenceInputStream, și DataInputStream care nu au o versiune de tip Reader.

În cazul claselor pentru scriere, se pot observa următoarele corespondențe: