Citirea datelor din consolă și din fișiere – Variantele C și C++
În C++ putem lucra cu fișiere de intrare și de ieșire. În acest articol voi prezenta cum se citesc și cum se afișează datele, atât în consolă cât și în fișiere text. Voi descrie și varianta C (valabilă și în C++), pentru că sub Windows e mult mai rapidă, și poate face diferența la concursuri.
Atunci când trebuie specificată adresa unui fișier o putem face atât relativ, cât și absolut. Noi vom lucra doar cu fișiere ce se află în folder-ul sursei, așa că le vom specifica numai numele. Pentru a crea un fișier în CodeBlocks: File > New > Empty file (Ctrl+Shift+N).
Varianta C++
C++ folosește conceptul de stream-uri pentru citirea și scrierea datelor din/ în fișiere (tastatura și consola sunt tot fișiere). Biblioteca iostream
definește 4 obiecte globale de tip stream, considerate sursele și destinațiile standard ale caracterelor:
Stream | Descriere |
---|---|
cin | standard input stream |
cout | standard output stream |
cerr | standard error output stream |
clog | standard logging output stream |
cerr
și clog
fac practic aceleași lucruri ca cout
, însă au teoretic alt rol: cerr
se folosește pentru afișarea erorilor, iar clog
pentru afișarea log-urilor. Implicit, în majoritatea mediilor de programare, toate cele trei obiecte afișează datele în consolă, însă pot fi setate să o facă în ferestre diferite.
Ce înseamnă de fapt că prin cin
citim date din standard input stream? NU înseamnă că le citim neapărat de la tastatură, ci de unde spune variabila stdin
, care este setată implicit pentru tastatură. Similar, cout
scrie date unde îi spune stdout
, care este setată implicit pentru consolă.
Stream-ul standard pentru output (cout
)
Pentru scrierea datelor prin cout
, se folosește operatorul de inserție (acel shift left de la Operatori pe biți). Iată câteva exemple:
cout << "string"; // Afișează „string”.cout << 618; // Afișează „618”.cout << x; // Afișează valoarea variabilei x.
Putem scrie o singură instrucțiune pentru a afișa mai multe expresii, asta pentru că operatorul shift left returnează o referință la operandul stâng, iar expresia se evaluează de la stânga la dreapta. De exemplu:
cout << "Ati introdus numarul " << x << "!\n";
Pentru a trece la o linie nouă recomand folosirea caracterului '\n'
. O alternativă (ineficientă) este folosirea manipulatorului endl
, din biblioteca ostream
, care după ce inserează '\n'
, golește buffer-ul stream-ului (conținutul care încă nu a apucat să fie scris va fi afișat), ceea ce de cele mai multe ori este inutil. Exemplu care folosește endl
:
cout << "Hello World!" << endl;
Pentru a seta numărul de zecimale cu care să fie afișate numerele reale, se folosesc modificatorul fixed
(din biblioteca ios
) și funcția setprecision(nrZecimale)
(din biblioteca iomanip
) în felul următor:
double x = 1.618;cout << fixed << setprecision(2) << x; // 1.61cout << setprecision(4) << x; // 1.6180
fixed
e de ajuns să fie scris o singură dată. Toate numerele reale afișate după apelul funcției setprecision
vor fi scrise cu numărul respectiv de zecimale, până la un alt apel al funcției (dacă atunci se va schimba numărul de zecimale).
Stream-ul standard pentru input (cin
)
Pentru citirea datelor prin cin
se folosește operatorul de extracție (shift right).
cin >> x; // Citește valoarea lui x.
Pentru a citi mai multe variabile de tip întreg, real sau boolean, valorile acestora trebuie să fie separate în fișierul de intrare prin caractere albe (spații, tab-uri, enter-uri etc.).
Când se citesc variabile de tip caracter, caracterele albe sunt ignorate. Pentru a evita asta putem folosi metoda get(variabilaDeTipCaracter)
, care este valabilă și pentru obiectele din clasa ifstream
:
cin.get(c);
La fel ca la cout
, instrucțiunea următoare este corectă:
cin >> a >> b >> c;
Exemplu de program ce folosește cin
și cout
:
#include <iostream>using namespace std;
int a, b;
int main() { cout << "Introduceti valoarea lui a: "; cin >> a; cout << "Introduceti valoarea lui b: "; cin >> b; cout << "a + b = " << a + b << '\n'; return 0;}
Fișiere în C++
Pentru a lucra cu alte fișiere, putem crea un obiect din clasa ifstream
(dacă fișierul este de intrare), sau ofstream
(dacă e de ieșire). Iată deci cum se creează un obiect fișier de intrare și unul de ieșire:
ifstream fin("adresaFisierDeIntrare");ofstream fout("adresaFisierDeIesire");
Evident, în loc de fin
și fout
putem folosi și alți identificatori, căci acestea sunt numele obiectelor. Pentru a efectua operații de citire/ scriere cu aceste obiecte se procedează exact ca la cin
/cout
. Pentru a închide fișierele (e recomandat să facem asta după ce am terminat de lucrat cu ele) folosim metoda close
:
fin.close();fout.close();
Iată un exemplu de program ce folosește fișiere C++:
#include <fstream>using namespace std;
ifstream fin("file.in");ofstream fout("file.out");
int a, b;
int main() { fin >> a >> b; fout << a + b << '\n';
fout.close(); return 0;}
Metoda flush
Metoda flush()
este un membru al clasei ofstream
, deci expresii ca cout.flush()
și fout.flush()
sunt valide. Această metodă golește buffer-ul stream-ului respectiv. Adică ceea ce face și endl
după afișarea enter-ului. Este o funcție importantă la concursuri. În problemele interactive trebuie să trimiți date evaluatorului pentru a primi un răspuns, iar acest proces se repetă de câte ori este nevoie. Această interacțiune nu se poate realiza decât prin apelarea metodei flush()
; altfel evaluatorul ar primi datele abia la finalul programului, și n-ar mai putea răspunde. Exemplu:
cout << "Astept raspunsul...\n";cout.flush();cin >> raspuns;
Și iată un truc pentru concursuri. Dacă se cere, de exemplu, să se afișeze lungimea unui șir, iar apoi șirul respectiv, și se acordă punctaj separat pentru cele două linii, dar simți că pe unele teste e posibil să-ți crape programul gând generezi șirul, poți da un flush()
după ce afișezi lungimea lui. Astfel, poți fi sigur că evaluatorul primește răspunsul măcar pentru prima cerință, și că vei primi punctajul pentru aceasta
Varianta C
Cum în C nu există OOP, scrierea și citirea datelor se face prin funcții. De aceea este mai eficient să folosim această variantă (este suportată și în C++). Pentru fișierele de intrare/ ieșire standard folosim funcțiile scanf
și printf
. Legat de fișierele standard, e aceeași fază ca la stream-uri: Se citește/ scrie în ce spun macro-urile stdin
și stdout
. Iată întâi câteva exemple:
int a; short int b; float c;scanf("%d %hd %f", &a, &b, &c);printf("Valoarea lui a este %d!\n", a);
Funcția scanf
Primul parametru al funcției este un șir de caractere ce reprezintă formatul în care se citesc datele. De exemplu, dacă se spune că se citesc trei numere întregi separate prin câte un spațiu vom scrie "%d %d %d"
.
Notațiile cu %
se numesc specificatori de format, și diferă în funcție de tipul variabilei care se citește. Pentru int
se folosește %d
(de la decimal), pentru float
%f
, iar pentru char
%c
. În cazul variabilelor de tip char
, se citesc și caracterele albe.
Există și modificatori: h
pentru short
(int
), l
pentru long
(int
) și double
, ll
pentru long long
(int
) și L
pentru long
(double
). Deci %lf
va fi double
, %Llf
long double
, %hd
short int
etc. Pentru a include caracterul %
în format, se folosește %%
, pentru a nu se confunda cu un specificator.
Următorii parametri reprezintă adresele (sunt marcate cu &
) variabilelor care sunt citite. Aflați mai multe despre funcția scanf
aici.
Funcția printf
Este destul de asemănătoare. Primul parametru este formatul în care se afișează datele, iar restul valorile variabilelor ce trebuie afișate. Formatul poate fi doar un string, caz în care nu este urmat de alți parametri.
Fișiere în C
Pentru a folosi alte fișiere în C, vom declara niște pointeri de tip FILE
:
FILE *fin, *fout;
Pentru a lucra cu aceste fișiere, trebuie să le deschidem mai întâi:
fin = fopen("adresaFisierului", "r");fout = fopen("adresaFisierului", "w");
Funcția fopen
primește ca prim parametru adresa fișierului, iar al doilea este un string care reprezintă modul în care se accesează fișierul. Modurile sunt "r"
(read), "w"
(write), "a"
(append, adică scrie la sfârșitul fișierului, fără a șterge conținutul lui), "r+"
(read/update), "w+"
(write/update) și "a+"
(append/update).
La fel ca atunci când creăm un obiect de tip ofstream
, la apelul fopen("fisier", "w")
, se șterge tot conținutul lui, iar dacă fișierul nu există, se creează.
Funcția care închide fișierul este fclose
:
fclose(pointerFisier);
Pentru a citi și a scrie date se folosesc funcțiile fscanf
și fprintf
, care sunt exact ca scanf
și printf
, doar că au încă un parametru la început (fișierul):
fscanf(fin, "format", ...);fprintf(fout, "format", ...);
Funcția freopen
Funcția freopen("adresaFisier", "modAcces", fisier)
schimbă adresa fișierului și modul său de acces pentru pointer-ul de tip FILE
fisier
. Este utilă deoarece dacă o folosim ca mai jos, putem lucra cu fișiere fără să le „declarăm”, ci doar prin cin
, cout
, scanf
, printf
:
freopen("fisierDeIntrare", "r", stdin);freopen("fisierDeIesire", "w", stdout);
Exemplu de program ce folosește fișiere C:
#include <cstdio>
FILE *fin, *fout;int a, b, c;
int main() { fin = fopen("file.in", "r"); fout = fopen("file.out", "w");
fscanf(fin, "%d %d %d", &a, &b, &c); fprintf(fout, "%d\n", a + b + c);
fclose(fout); return 0;}
Funcția fflush
Funcția fflush(FILE*)
face exact ceea ce face și metoda flush()
, dar pentru fișierele din C. Deci, primește ca parametru un pointer la FILE
. Exemplu de apel: fflush(stdout)
.
Obiectele cin
, cout
, cerr
și clog
sunt incluse în header-ul iostream
; ifstream
și ofstream
în fstream
, iar funcțiile scanf
, printf
, și tot ce ține de fișiere C în cstdio
.
Următorul tutorial C++ este Instrucțiunile alternative în C++: if și switch. Dacă aveți vreo întrebare despre citirea/ afișarea datelor din/ în consolă/ fișiere în C/C++, nu ezitați să o adresați mai jos printr-un comentariu