Fluxuri de intrare/ieșire și fișiere

Introducere

În majoritatea aplicațiilor, este necesar să se transmită date între diferite componente cum sunt: memoria internă, tastatura, ecranul, fișierele de pe disc, rețeaua de calculatoare etc. Poate fi necesar, de asemenea, să se transmită date între două aplicații sau între două fire de execuție ale aceleeași aplicații. În limbajul Java, fluxul (engleză: stream) este o cale de comunicație între o sursă de date și o destinație (Figura 1).

- Figura 1 -

Fluxul este un concept situat pe un nivel înalt de abstractizare, fiind privit ca o simplă succesiune de octeți sau de caractere care se transmite între sursă și destinație. Nu prezintă importanță nici natura sursei sau a destinației, nici modul în care trebuie interpretată secvența de octeti sau de caractere respectivă.  De exemplu, un grup de 32 de octeți transmiși între sursă și destinație poate să reprezinte 32 de caractere ASCII sau 16 caractere Unicode sau 8 numere de tip int sau 4 numere de tip double etc. Această abstractizare permite să se trateze în mod unitar toate tipurile de transmisii de date.

Se disting doua feluri de fluxuri: de ieșire și de intrare. Pentru un proces dat, toate fluxurile transmise de acesta către exterior se numesc fluxuri de ieșire, iar cele primite din exterior se numesc fluxuri de intrare. În consecință, același flux este de ieșire în raport cu sursa și de intrare în raport cu destinația.

Principalele operații care au loc asupra unui flux sunt:
 
La sursă (flux de ieșire) La destinație (flux de intrare)
- Deschiderea fluxului
- cât timp (există date de transmis)
        scriere în flux
-Închiderea fluxului
-Deschiderea fluxului
- cât timp (există date de citit)
        citire din flux
-Închiderea fluxului

Programatorului procesului sursă îi revine obligația de a pregăti, sub forma unei succesiuni de caractere sau de octeți, datele care urmează a fi transmise pe fluxul de ieșire. Interpretarea și tratarea datelor conținute în flux se face de către procesul de destinație, deci întreaga responsabilitate îi revine celui care programează acest proces. Desigur că este necesar ca interpretarea datelor din flux la destinație să corespundă cu cea de la sursă. Programarea operațiilor de intrare/ieșire poate fi destul de complicată, dar în limbajul Java ea este totuși ușurată de existența unui număr destul de mare de clase cu această destinație, grupate în pachetul java.io.

Pachetul java.io

În Java 2 SDK se consideră că fluxurile pot fi  de caractere saude octeți. În primul caz, de la sursă la destinație se transmite o succesiune de caractere Unicode (de câte 16 biți), iar în al doilea caz - o succesiune de octeți (de 8 biți). În mod corespunzător, pentru fiecare din cele două categorii de fluxuri există câte o ierarhie de clase de fluxuri de intrare și o ierarhie de clase de fluxuri de iesire. Pentru fluxurile de caractere, rădăcinile ierarhiilor de clase sunt clasele abstracte Reader și Writer.Pentru fluxurile de octeți, rădăcinile acestor ierarhii  sunt clasele abstracte InputStream și OutputStream.  Aceste ierarhii de clase sunt reprezentate în figurile 2, 3, 4 și 5. În afară de cele patru ierarhii menționate, în pachetul java.io există și clase auxiliare și interfete. Distingem trei tipuri de clase, care sunt reprezentate în aceste figuri prin culori diferite:
    - clase abstracte (culoare albastră);
    - clase care efectuează operațiile de intrare sau de ieșire propriu-zise (culoare verde) și  modelează sursele sau destinațiile fluxurilor (engleză: Data Sink Streams);
    - clase care efectuează unele operații de transformare a datelor de pe flux (culoare violet) și reprezinta "fluxuri de prelucrare" (engleză: Processing Streams).

- Figura 2 - Ierarhia claselor pentru fluxuri de intrare de caractere
 

- Figura 3 - Ierarhia claselor pentru fluxuri de ieșire de caractere

- Figura 4 - Ierarhia claselor pentru fluxuri de intrare de octeți
 

- Figura 5 - Ierarhia claselor pentru fluxuri de ieșire de octeti

Se observă corespondența între clasele de fluxuri de intrare și de ieșire în cadrul aceleeași categorii de fluxuri. De exemplu, în cazul fluxurilor de caractere, unui BufferedReader îi corespunde un BufferedWriter, unui CharArrayReader îi corespunde un CharArrayWriter etc. În mod similar, în cazul fluxurilor de octeți, unui FileInputStream îi corespunde un FileOutputStream, unui PipedInputStream îi corespunde un PipedOutputStream etc. Aceasta reflectă "simetria" operațiilor de intrare și de ieșire. Există totuși și clase de intrare fără corespondent la ieșire (de exemplu StringBufferInputStream sau PushbackInputStream) sau clase de ieșire fără corespondent la intrare (de exemplu PrintStream).
 
În JDK 1.0 existau numai clasele de fluxuri de octeți. Fluxurile de caractere au fost introduse începand cu JDK 1.1. În prezent se folosesc ambele categorii de fluxuri. Pentru transmiterea textelor sau a datelor reprezentate în format extern și codificate în Unicode este preferabil, evident, să se folosească fluxuri de caractere. Pentru transmiterea datelor codificate binar sau a textelor în care caracterele sunt codificate pe un octet (de exemplu în codul ASCII) este preferabilă folosirea fluxurilor de octeți.

Fluxurile pot fi înlănțuite, astfel încât ieșirea unui flux poate fi intrare a altui flux. Astfel, în Figura 6 este dată schema unei înlănțuiri de fluxuri de intrare: ieșirea fluxului InputStream (sub forma de fluxului de octeți flux1) este dată la intrarea fluxului InputStreamReader, care o convertește în fluxul de caracrtere flux2, iar acesta - la rândul lui - este preluat fe fluxul de intrare de caractere cu zonă tampon BufferedReader.

- Figura 6- Înlănțuire de fluxuri de intrare

În Figura 7 este reprezentată o înlănțuire similară de fluxuri de ieșire: ieșirea fluxului de caractere BufferedWriter este aplicată la intrarea fluxului OutputStreamWriter, iar acesta face conversia într-un flux de octeți, pe care îl transmite la un OutputStream (de exemplu la un PrintStream sau la un FileOutputStream).

- Figura 7 - Înlănțuire de fluxuri de ieșire

Având în vedere complexitatea programării operațiilor de intrare/ieșire, în cele ce urmează vom căuta să facem o prezentare graduală, de la simplu la complex. Vom începe însă cu prezentarea comparativă a claselor abstracte care constituie rădăcinile celor patru ierarhii de clase de fluxuri. Aceasta ne va permite să ne formăm o vedere de ansamblu asupra principalelor metode folosite în lucrul cu fluxuri.



© Copyright 2000 - Severin BUMBARU, Universitatea "Dunărea de Jos" din Galați