Fișiere text

ifstream și ofstream, citirea până la sfârșitul fișierului și greșelile care costă puncte deși programul e corect.

De ce

La Subiectul III, ultima problemă îți dă de obicei datele într-un fișier, de exemplu bac.txt sau bac.in, și îți cere să afișezi rezultatul pe ecran. Uneori cere și scrierea într-un fișier. Citește atent ce fișiere numește enunțul și unde trebuie să apară rezultatul.

Deschiderea

#include <fstream>
using namespace std;

ifstream fin("date.in");        // pentru citire
ofstream fout("date.out");      // pentru scriere

ifstream = input file stream, ofstream = output file stream. Numele variabilelor (fin, fout) le alegi tu, iar numele fișierelor le iei din enunț.

De aici încolo, totul funcționează exact ca la cin și cout:

int n;
fin >> n;
fout << n * 2;

Folosești aceleași săgeți și aceleași tipare de citire, iar spațiile se tratează la fel. Nu ai nimic nou de învățat, doar schimbi numele.

La final:

fin.close();
fout.close();

Două greșeli care costă puncte

Numele fișierului scris altfel decât în enunț. Dacă cerința spune bac.in și tu scrii bac.txt sau Bac.in, programul nu găsește nimic. Copiază numele exact, cu tot cu extensie.

Afișarea în alt loc decât cere enunțul. Dacă enunțul cere rezultatul într-un fișier și ți-a rămas un cout de la testare, fișierul de ieșire iese gol. Verifică înainte de predare că fiecare ieșire merge acolo unde cere enunțul: pe ecran cu cout, în fișier cu fout.

În ambele cazuri, un program altfel corect pierde puncte la bac, pentru că nu face ce cere enunțul. Pe platformă, programul e rulat pe teste cu fișierele din enunț, așa că aici o astfel de greșeală îl face să pice testele.

Citirea până la sfârșit

Când enunțul nu spune câte valori sunt (se întâmplă foarte des la fișiere):

int x;
while (fin >> x) {
    // prelucrez x
}

fin >> x devine fals când nu mai are ce citi. E cel mai curat mod și pe acesta ar trebui să-l folosești.

Pentru linii de text:

char linie[256];
while (fin.getline(linie, 256)) {
    // prelucrez linia
}

Citirea unei matrice din fișier

int n, m;
fin >> n >> m;
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        fin >> a[i][j];

Nu contează cum sunt aranjate numerele în fișier, pe o linie sau pe mai multe. >> sare peste spații și linii noi la fel ca cin.

Două parcurgeri ale aceluiași fișier

Uneori trebuie să treci de două ori prin date, de exemplu ca să afli media și apoi câte valori o depășesc.

Dacă valorile încap într-un vector, citește-le o dată și lucrează în memorie. Dacă nu, poți închide și redeschide fișierul:

fin.close();
fin.open("date.in");

Aproape întotdeauna e mai bună prima variantă.

Verificarea deschiderii

Verificarea te ajută cât scrii programul, ca să deosebești un „fișier lipsă" de un „algoritm greșit":

ifstream fin("date.in");
if (!fin) {
    cout << "nu pot deschide fisierul";
    return 1;
}

La bac nu se cere, dar dacă programul tău „nu afișează nimic", de aici începi.

Un exemplu complet

Fișierul numere.in conține pe prima linie un număr natural n, iar pe a doua linie n numere întregi. Să se scrie în numere.out cel mai mare număr par din șir, sau -1 dacă nu există.

#include <fstream>
using namespace std;

ifstream fin("numere.in");
ofstream fout("numere.out");

int main() {
    int n;
    fin >> n;

    int maxim = -1;
    for (int i = 1; i <= n; i++) {
        int x;
        fin >> x;
        if (x % 2 == 0 && (maxim == -1 || x > maxim))
            maxim = x;
    }

    fout << maxim;

    fin.close();
    fout.close();
    return 0;
}

Condiția maxim == -1 || x > maxim rezolvă cazul în care toate numerele pare sunt negative. Dacă am fi pornit de la 0 și am fi comparat simplu, -4 nu ar fi fost reținut niciodată.

Observă și că nu e nevoie de vector. Se cere un singur rezultat, calculat din toate valorile, deci prelucrezi fiecare valoare pe măsură ce o citești.

De reținut

  • ifstream și ofstream se folosesc identic cu cin și cout.
  • Numele fișierelor se copiază exact din enunț.
  • Nu lăsa cout-uri de test în soluția finală.
  • while (fin >> x) citește până la sfârșitul fișierului.
  • Închide fișierele la final.
  • Dacă nu ai nevoie de valori după citire, nu-ți trebuie vector.