Sunteți pe pagina 1din 14

Curs: Core Java Programming

Module: Gestionarea sistemului de fișiere


Unitate: Fluxurile de date

Unicul mod în care datele pot intra sau ieşi dintr-o aplicaţie Java este
prin folosirea fluxului, respectiv a Stream-ului. Când vorbim despre
fluxuri, întotdeauna subînţelegem două tipuri, de ieşire şi de intrare.
De exemplu, comanda folosită frecvent, println, este de fapt o
comandă care emite textul în fluxul de ieşire. Noţiunea de flux nu se
numeşte aşa din întâmplare, ci din cauză că datele într-adevăr "curg"
către ieşire sau dinspre intrare. Mai precis, datele se deplasează
secvenţial, secțiune cu secțiune. În funcţie de natura fluxului, părţile
acestea sunt byţi sau caractere, tipuri primitive sau obiecte.

Indiferent de tipul lor sau de modul în care funcționează, toate fluxurile


unui program reprezintă același lucru: o secvență de date. Un program
folosește fluxul de intrare pentru a citi datele dintr-o sursă.

Imaginea 25.1 Fluxul de intrare

Fluxurile de ieșire se folosesc pentru a introduce datele în locația de


destinație.

© Copyright Link group 1 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Imaginea 25.2 Fluxul de ieșire

Toate clasele legate de fluxuri se află în pachetul java.io

Cele două clase de bază pentru gestionarea fluxului suntInputStream


şi OutputStream. Aceste două clase sunt abstracte şi le moştenesc
toate clasele importante pentru gestionarea fluxurilor.

Imaginea 25.3 Structura de moștenire a claselor fluxurilor

Fluxurile de octeți (Byte Streams)

Un program folosește fluxurile de octeți pentru a scrie sau pentru a citi


ceva byte cu byte. Clasele FileInputStream și FileOutputStream sunt un
exemplu de astfel de fluxuri. Procedura de gestionare a fluxurilor este

© Copyright Link group 2 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

similară pentru toate tipurile de fluxuri: mai întâi este inițializat fluxul,
iar apoi sunt descărcate datele din el, secțiune cu secțiune sau, în
acest caz, byte cu byte.

Următorul exemplu ne arată cum putem introduce un text într-un fișier


cu ajutorul unui flux:

public static void main(String[] args) {

String myText = "Hello World";

try (FileOutputStream fs = new FileOutputStream("myFile.txt");) {


fs.write(myText.getBytes());
} catch (IOException exc) {
System.out.println(exc);
}
}

Pentru afişarea mesajului unei excepţii se poate utiliza şi fluxul


System.err. În cazul acestui exemplu în blocul catch se poate utiliza în
loc
System.err.println(exc);

În codul anterior, am definit un text în cadrul variabilei myText, pe care


dorim să îl scriem într-un fișier. Mai apoi, am creat obiectul
FileOutputStream, al cărui constructor conține denumirea fișierului în
care dorim să scriem. De asemenea, am definit blocurile try/catch, în
cazul în care intervine o eroare în timpul scrierii. Este foarte important
să închidem fluxul după ce am terminat de scris, fiindcă fișierul va fi
accesibil sistemului numai după ce fluxul este închis. De asemenea,
doar atunci va fi conținutul complet al fluxului stocat în siguranță în
fișier. În exemplul de mai sus, am utilizat blocul ARM (Automatic
Resource Management), care este un accesoriu disponibil începând cu
Java versiunea 7. Vom defini resursele care trebuie închise între
parantezele blocului try. În acest mod, resursele sunt eliberate în
momentul în care am terminat de scris în fișier.

© Copyright Link group 3 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Cititul dintr-un fișier este destul de simplu:

public static void main(String[] args) {

try (FileInputStream fs = new FileInputStream("myFile.txt");) {


int content = fs.read();
while (content != -1) {
System.out.print((char) content);
content = fs.read();
}
} catch (IOException exc) {
System.out.println(exc);
}
}

În acest exemplu, am utilizat clasa FileInputStream. Octeții citiți i-am


stocat în conținutul variabilei integer și am emis la ieșire conținutul
convertit în caractere, până când este citită valoarea -1. -1 înseamnă
întotdeauna finalul fluxului. Închidem fluxul și eliberăm resursele în
același mod ca în exemplul anterior.

Utilizând fluxurile de octeți, obținem o utilizare de nivel scăzut a


fluxurilor, iar dacă nu avem o nevoie specială pentru o astfel de
scriere/citire, este mai bine să utilizăm alte tipuri de fluxuri, mai
specializate. În acest exemplu, am efectuat citirea și scrierea unui text,
așadar unul dintre tipurile următoare de fluxuri va fi mult mai potrivit.

Probabil vă întrebați de ce am discutat despre acest tip de flux (fluxul


de octeți) în primul rând. Motivul pentru care am făcut acest lucru este
pentru că toate celelalte tipuri de fluxuri au fost dezvoltate pe baza
fluxului de octeți, după cum puteți vedea în Figura 22.3.

Fluxurile de caractere (Character Streams)

În Java, caracterele sunt gestionate cu ajutorul standardului UNICODE.


Spre deosebire de fluxurile de byți, fluxurile de caractere gestionează

© Copyright Link group 4 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

caracterele, după cum o spune și denumirea lor. Toate fluxurile de


caractere moștenesc clasele Reader și Writer, în timp ce FileReader și
FileWriter sunt clase specializate pentru uzul direct. Să privim exemplul
anterior, dar de această dată realizat cu ajutorul fluxurilor de
caractere.

public static void main(String[] args) {

String myText = "Hello World";

try (FileWriter fw = new FileWriter("myFile.txt");) {


fw.write(myText);
} catch (IOException exc) {
System.out.println(exc);
}
}

Putem citi conținutul fișierului în modul următor. Bineînțeles, citim


caracter cu caracter:

public static void main(String[] args) {

try (FileReader fr = new FileReader("myFile.txt");) {


int c;
while ((c = fr.read()) != -1) {
System.out.print(c);
}

} catch (IOException exc) {


System.out.println(exc);
} finally {
System.out.println();
}
}

© Copyright Link group 5 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Dacă rulăm codul anterior, vom vedea că la ieșire vom obține un șir de
valori întregi ce reprezintă codurile UNICODE pe care caracterele le au
în sistem.

Dacă adăugăm casting-ul simplu, respectiv conversia din tipul int în


tipul char, vom obține la ieșire un print într-o formă inteligibilă:

System.out.print((char)c);

Bineînțeles, foarte rar vom utiliza această tehnică de citire a unui fișier
de text. O metodă mult mai eficientă este citirea linie după linie:

public static void main(String[] args) {

try (BufferedReader br = new BufferedReader(new FileReader("myFile.txt"));) {


String l;
while ((l = br.readLine()) != null) {
System.out.println(l);
}

} catch (IOException exc) {


System.out.println(exc);
}
}

Cu codul anterior, fișierul de text va fi citit linie după linie. În codul


anterior se află un termen necunoscut: BufferedReader. În continuare
vom explica ce reprezintă această clasă.

Fluxurile care folosesc buffer-ul (Buffered Streams)

Exemplele de până acum au reprezentat așa-numita citire și scriere


”unbuffered”. Aceasta înseamnă că fiecare cerere pentru citire sau
scriere rezultă cu o cerere de a accesa disk-ul, fie el local sau de rețea.

© Copyright Link group 6 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Este evident că acest lucru necesită o mare angajare a resurselor. Spre


deosebire de acestea, în fluxurile ”buffered”, datele sunt descărcate o
singură dată și sunt stocate într-o secțiune separată numită buffer, de
unde sunt mai apoi utilizate în funcție de nevoie. Prin urmare, depozitul
este stocat doar când buffer-ul este gol, pentru a citi datele, și doar
când buffer-ul este plin, pentru a scrie datele.

Este evident că utilizând fluxurile ”buffered”, putem accelera


semnificativ procesul de citire și de scriere. În loc să citim byte cu byte,
datele sunt citite în blocuri mai mari. Acest lucru este în mod special
eficient dacă accesăm o cantitate mai mare de date.

Există patru clase de fluxuri ”buffered” ce prezintă interes pentru noi:

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

Până în acest moment, am văzut fluxuri în Java care citesc/scriu datele


byte cu byte sau caracter cu caracter. Tocmai aceasta este diferența
dintre perechile de clase enumerate mai sus. Am folosit deja clasa
BufferReader în exemplul fluxurilor de caractere. Această clasă permite
citirea directă a caracterelor, iar în cazul nostru de citire a unui fișier
de text, reprezintă o soluție foarte bună. Spre deosebire de clasele
menționate anterior, BufferedInputStream / BufferedOutputStream
operează cu date brute sub formă de octeți.

Indiferent ce tip de clasă utilizați, acestea se folosesc întotdeauna


drept clase de tip wrapper, respectiv sunt un fel de ambalaj pentru
utilizarea claselor ”unbuffered” menționate anterior.

© Copyright Link group 7 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

BufferedInputStream input = new BufferedInputStream(


new FileInputStream("myFile.txt "));

sau

BufferedReader input = new BufferedReader( new


FileReader("myFile.txt "));

Acordați atenție faptului că în inițializarea de mai sus, în constructorul


fluxurilor ”buffered”, definim clasele de fluxuri despre care am
discutat, respectiv clasele de fluxuri ce citesc datele una câte una.
Trebuie să fiți atenți dacă doriți să utilizați fluxurile de caractere și să
utilizați un buffer ce operează cu caractere, fiindcă în caz contrar, nu
veți putea instanția obiectul de flux.

Imaginea 25.4 Utilizare necorespunzătoare a fluxurilor buffered

Fluxurile de date

Fluxurile de date ne permit să scriem și să citim tipurile primitive de


date și stringurile. Clasele utilizate în acest caz sunt: DataInputStream
și DataOutputStream. Să vedem cum putem scrie într-un fișier mai
multe constante ale unor tipuri diferite de date.

public static void main(String[] args) {

int variableA = 13;


double variableB = 13.13;
String variableC = "Hello!";

try (DataOutputStream out = new DataOutputStream(new FileOutputStream("myFile.txt"));


){

© Copyright Link group 8 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

out.writeInt(variableA);
out.writeDouble(variableB);
out.writeUTF(variableC);
} catch (IOException exc) {
System.out.println(exc);
}
}

După cum puteți vedea, aceste clase reprezintă un wrapper al claselor


ce gestionează octeții. După ce am terminat de scris, putem citi
variabilele din fișier.

public static void main(String[] args) {

try (DataInputStream in = new DataInputStream(new FileInputStream("myFile.txt"));) {

System.out.println(in.readInt());
System.out.println(in.readDouble());
System.out.println(in.readUTF());

} catch (IOException exc) {


System.out.println(exc);
}
}

Citirea și scrierea din consolă

Există mai multe metode de a citi și de a scrie date din consolă.

Încă de la început, utilizăm metoda printIn pentru a scrie datele în


consolă.

System.out.println("This is text.");

© Copyright Link group 9 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Pe lângă această metodă, putem de asemenea să utilizăm metoda


print.

System.out.print("This is text.");

Diferența dintre aceste două metode este aceea că metoda printIn se


mută pe o nouă linie după ce a terminat de scris un text, în timp ce
metoda print nu face acest lucru. Vom ilustra acest lucru în următorul
exemplu:

public class MyJavaProgram {


public static void main(String[] args) {

System.out.print("This is text.");
System.out.print("This is text.");

System.out.println("This is text.");
System.out.println("This is text.");
}
}

La ieșire vom obține următorul text:

This is text.This is text.This is text.


This is text.

Haideți acum să vedem cum putem citi datele din consolă. Prima
modalitate este să utilizăm BufferedReader.

public static void main(String[] args) {

System.out.println("Enter something here : ");

try(BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.


in));) {

© Copyright Link group 10 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

String s = bufferRead.readLine();
System.out.println("You entered: " + s);

} catch (IOException e) {
System.out.println(e);
}
}

În codul de mai sus, linia citită este întotdeauna sub forma unui string,
prin urmare trebuie să efectuăm parsarea dacă dorim să obținem date
de un alt tip. Putem întâmpina o problemă dacă, de exemplu, așteptăm
o valoare întreagă de la consolă, însă introducem un tip
necorespunzător. Începând cu Java versiunea 5, există clasa Scanner,
creată pentru a se ocupa de problemele de acest tip.

public static void main(String[] args) {


System.out.print("Enter a number: ");

Scanner scan = new Scanner(System.in);

if (scan.hasNextInt()) {
int number = scan.nextInt();
System.out.println(number);
}
else
{
System.out.println("You must enter number!");
}
}

În acest mod, verificăm cu ușurință dacă în consolă a fost introdus un


număr, iar dacă a fost, efectuăm citirea și îl plasăm într-o variabilă de
tip număr întreg.

© Copyright Link group 11 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Exerciții

Exercițiul 1

Problemă:

Avem fișierul users.txt cu următorul conținut:

id:01|firstName:Petar|lastName:Petrovic|jmbg:1234567890123
id:02|firstName:Jovan|lastName:Jovanovic|jmbg:1234567890124
id:03|firstName:Nikola|lastName:Nikolic|jmbg:1234567890125

Creați o aplicație care va citi fișierul și va afișa conținutul în următorul


format:

User Id: 01
First name: Petar
Last name: Petrovic
Jmbg: 1234567890123
------------------------------------
User Id: 02
First name: Jovan
Last name: Jovanovic
Jmbg: 1234567890124
.....

Soluție:

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new FileReader("users.txt"));
String line;
while((line=br.readLine())!=null)
{
String[] user = line.split("\\|");
System.out.println("User Id: " + user[0].split("\\:")[1]);
System.out.println("First name: " + user[1].split("\\:")[1]);

© Copyright Link group 12 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

System.out.println("Last name: " + user[2].split("\\:")[1]);


System.out.println("Jmbg: " + user[3].split("\\:")[1]);
System.out.println("------------------------------------");
}
br.close();
}
}

Exercițiul 2

Creați o aplicație care concatenează fișierele (pune două fișiere într-


unul singur). Denumirile acestor fișiere sunt: firstFile.txt și
secondFile.txt. Uniți conținuturile acestor două fișiere și puneți-le în
fișierul denumit thirdFile.txt.

Soluția 1:

import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
String tmpLine;
BufferedWriter bw = new BufferedWriter(new FileWriter("thirdFile.txt"));
BufferedReader br = new BufferedReader(new FileReader("firstFile.txt"));
while((tmpLine=br.readLine())!=null)
bw.write(tmpLine+"\n");
br.close();
br = new BufferedReader(new FileReader("secondFile.txt"));
while((tmpLine=br.readLine())!=null)
bw.write(tmpLine);
br.close();
bw.close();
}
}

© Copyright Link group 13 / 14


Curs: Core Java Programming
Module: Gestionarea sistemului de fișiere
Unitate: Fluxurile de date

Soluția 2:

import java.io.*;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) throws IOException{

String tmpLine;
BufferedWriter bw = new BufferedWriter(new FileWriter("thirdFile.txt"));
ArrayList<BufferedReader> filesToRead = new ArrayList<BufferedReader>();
filesToRead.add(new BufferedReader(new FileReader("firstFile.txt")));
filesToRead.add(new BufferedReader(new FileReader("secondFile.txt")));
for(BufferedReader br : filesToRead)
{
while((tmpLine=br.readLine())!=null)
bw.write(tmpLine+"\n");
br.close();
}
bw.close();
}
}

© Copyright Link group 14 / 14

Powered by TCPDF (www.tcpdf.org)

S-ar putea să vă placă și