Citirea datelor din consolă și din fișiere – Variantele C și C++

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:

StreamDescriere
cinstandard input stream
coutstandard output stream
cerrstandard error output stream
clogstandard 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.61
cout << 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 :yey:

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 :smile:

Mulțumesc că ai citit acest articol.
Dacă vrei să susții blogul, poți cumpăra un abonament de 2$.

patreon

Lasă un comentariu!

0 comentarii