„Fișierul bac.txt conține un șir de cel mult un milion de numere naturale, separate prin câte un spațiu.” Observă ce lipsește din această frază: nu ți se spune câte numere sunt. La problema de eficiență de la BAC informatică situația e frecventă, iar soluția în C++ este o singură linie: while (f >> x). Mai jos vezi de ce funcționează, de ce varianta cu eof() e o capcană și, mai ales, cum prelucrezi valorile pe măsură ce le citești, fără să le memorezi.

Cum funcționează while (f >> x)

Expresia f >> x face două lucruri: încearcă să citească o valoare în x și apoi „returnează” fluxul f. Folosit într-o condiție, fluxul se evaluează la adevărat dacă ultima citire a reușit și la fals dacă a eșuat — de exemplu, pentru că nu mai există date în fișier.

ifstream f("bac.txt");
int x;
while (f >> x)
{
    // aici x este sigur o valoare citită corect
}

Ordinea e cea corectă: întâi citești, apoi verifici dacă citirea a reușit, abia apoi prelucrezi. Corpul buclei se execută exact o dată pentru fiecare număr din fișier, indiferent dacă fișierul se termină sau nu cu spațiu ori cu linie nouă.

De ce nu while (!f.eof())

Varianta while (!f.eof()) { f >> x; ... } pare naturală, dar verifică sfârșitul fișierului înainte de citire. Indicatorul eof devine adevărat abia după ce o citire a încercat să treacă de sfârșit. Dacă fișierul se termină cu o linie nouă după ultimul număr (situație foarte comună), bucla mai face un pas: citirea eșuează, dar corpul se execută oricum, iar ultima valoare poate fi prelucrată de două ori sau se prelucrează o valoare care nu există. Rezultatul: o sumă greșită, un contor cu unu în plus. Folosește întotdeauna citirea ca și condiție.

Prelucrarea „din mers”: fără vector

Dacă nu știi câte numere sunt, iar ele pot fi un milion, nu are sens să le pui pe toate într-un tablou când cerința se poate rezolva dintr-o singură trecere. Majoritatea cerințelor se reduc la câteva variabile actualizate la fiecare citire.

Suma, numărul și maximul

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ifstream f("bac.txt");
    long long s = 0;
    int x, nr = 0, maxim = -1;
    while (f >> x)
    {
        s = s + x;
        nr++;
        if (x > maxim) maxim = x;
    }
    cout << nr << ' ' << s << ' ' << maxim;
    return 0;
}

Pentru fișierul 4 17 9 17 2 se afișează 5 49 17. Suma e declarată long long: un milion de numere de ordinul miliardelor depășesc ușor limita lui int. Maximul pornește de la −1, o valoare mai mică decât orice număr natural.

Valoarea anterioară: perechi de termeni consecutivi

Multe cerințe vorbesc despre „perechi de termeni aflați pe poziții consecutive”. Pentru ele păstrezi într-o variabilă ant valoarea citită la pasul anterior. Primul număr îl citești separat, înaintea buclei:

int ant, x, nr = 0;
f >> ant;
while (f >> x)
{
    if (x == ant) nr++;      // pereche de valori egale consecutive
    ant = x;
}

Pentru 3 3 5 5 5 2, perechile egale sunt (3,3), (5,5) și încă o dată (5,5) — nr = 3. Linia ant = x de la final este esențială: fără ea, compari mereu cu primul număr.

Cea mai lungă secvență cu o proprietate

Cerință clasică: „Afișați lungimea maximă a unei secvențe de numere pare aflate pe poziții consecutive în fișier.” Ții o lungime curentă, care crește cât timp proprietatea se păstrează și revine la 0 când e întreruptă, plus un maxim:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ifstream f("bac.txt");
    int x, lung = 0, lmax = 0;
    while (f >> x)
    {
        if (x % 2 == 0)
        {
            lung++;
            if (lung > lmax) lmax = lung;
        }
        else lung = 0;
    }
    cout << lmax;
    return 0;
}

Urmărește programul pe fișierul 2 4 7 6 8 10 3 12:

xpar?lunglmax
2da11
4da22
7nu02
6da12
8da22
10da33
3nu03
12da13

Se afișează 3, corespunzător secvenței 6 8 10. Pentru că maximul se actualizează imediat după creșterea lui lung, nu ai nevoie de o verificare suplimentară după buclă — o secvență care se termină chiar la sfârșitul fișierului e și ea luată în calcul.

Alte forme ale aceleiași idei

La BAC: la itemul 3 din Subiectul III, descrierea algoritmului (2 puncte în modelul actual — verifică ultimul model oficial pe subiecte.edu.ro) poate menționa direct ideea: „numerele se citesc pe rând și se prelucrează imediat, fără a fi memorate; se folosesc doar câteva variabile simple, iar fișierul este parcurs o singură dată”. Exact asta justifică eficiența ca timp și ca memorie.

Cazuri-limită de verificat

Înainte să predai, întreabă-te ce afișează programul dacă fișierul conține un singur număr sau dacă niciun număr nu are proprietatea cerută. În exemplul cu secvențe pare, pentru un fișier cu numai valori impare se afișează 0. Dacă enunțul cere în acest caz un mesaj (de exemplu „nu exista”), adaugă testul după buclă.

De reținut