„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:
| x | par? | lung | lmax |
|---|---|---|---|
| 2 | da | 1 | 1 |
| 4 | da | 2 | 2 |
| 7 | nu | 0 | 2 |
| 6 | da | 1 | 2 |
| 8 | da | 2 | 2 |
| 10 | da | 3 | 3 |
| 3 | nu | 0 | 3 |
| 12 | da | 1 | 3 |
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
- Perechi de valori pe linie:
while (f >> a >> b)citește câte două numere la fiecare pas. - Cuvinte: cu
char cuv[31],while (f >> cuv)citește cuvânt cu cuvânt. - Linii întregi:
while (f.getline(s, 256))citește linie cu linie. - Valori mici, multe: dacă numerele sunt dintr-un interval restrâns (de exemplu [0, 100]), combini citirea cu un vector de frecvență —
while (f >> x) fr[x]++;.
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
- ●
while (f >> x)citește și verifică în același timp; corpul se execută o dată pentru fiecare valoare. - ●
while (!f.eof())poate prelucra ultima valoare de două ori — evit-o. - ● Pentru termeni consecutivi, citește primul număr înainte de buclă și actualizează
ant = xla final. - ● Secvența maximă: un contor curent care se resetează și un maxim actualizat la fiecare creștere.
- ● Prelucrarea din mers înseamnă o singură parcurgere și memorie constantă — exact ce cere eficiența.