Fișiere
Prelucrarea datelor stocate în fișiere text și binare utilizând tehnici specifice limbajului de programare studiat.
Bacalaureat
Ce este un fișier și de ce diferă de memoria internă
Un fișier este o colecție de date memorată pe un suport extern, identificată printr-un nume și gestionată de sistemul de operare. Diferența esențială față de vectori sau matrice, cerută frecvent la teorie, este persistența: datele din memoria internă dispar la terminarea programului, cele din fișier rămân.
A doua diferență e la fel de importantă practic: dimensiunea unui fișier nu trebuie declarată dinainte. Un vector are un număr maxim de elemente fixat la compilare; într-un fișier poți scrie oricâte înregistrări încape pe disc. De aceea problemele de BAC care spun „numărul de valori nu este cunoscut” se rezolvă citind din fișier până la epuizare, nu alocând un vector uriaș.
Clasificarea de bază, după modul de reprezentare a datelor:
- fișiere text — informația e memorată ca succesiune de caractere, organizată pe linii separate prin marcaj de sfârșit de linie; pot fi citite cu orice editor de text. Numărul 1024 ocupă în fișier text 4 caractere, nu 4 octeți de întreg.
- fișiere binare — informația e memorată exact în reprezentarea internă din memorie; nu sunt lizibile într-un editor obișnuit, dar sunt mai compacte și se citesc mai rapid, fără conversii.
După modul de acces:
- acces secvențial — pentru a ajunge la componenta de pe poziția k trebuie parcurse toate componentele anterioare; e singurul mod rezonabil la fișierele text, unde liniile au lungimi diferite;
- acces direct (aleator) — se poate sări imediat la o anumită componentă, calculându-i poziția; posibil doar la fișiere binare cu înregistrări de aceeași dimensiune.
Orice prelucrare respectă aceeași succesiune obligatorie: deschidere, prelucrare, închidere. Închiderea nu e o formalitate — la scriere ea golește zona tampon (bufferul) pe disc; un program care se termină fără să închidă fișierul poate lăsa ultimele date nescrise.
Fișiere text: citire și scriere
În C++ lucrul cu fișiere se face prin fluxuri (stream-uri): ifstream pentru citire, ofstream pentru scriere, fstream pentru ambele. Fluxul se leagă de fișier prin constructor sau prin metoda open, iar la final se apelează close.
Reguli de deschidere care se transformă direct în puncte pierdute:
- deschiderea pentru scriere a unui fișier existent îl golește (trunchiază) implicit; pentru a adăuga la sfârșit se folosește modul append;
- deschiderea pentru citire a unui fișier inexistent eșuează; fluxul devine invalid, iar orice citire ulterioară nu face nimic. De aceea se testează întotdeauna reușita deschiderii înainte de prelucrare.
Citirea se face cu operatorul de extragere pentru valori separate prin spații sau linii noi, cu getline pentru o linie întreagă (inclusiv spațiile din interiorul ei) și cu metoda get pentru un singur caracter. Amestecarea lor cere atenție: după o citire cu operatorul de extragere, în flux rămâne marcajul de sfârșit de linie, iar un getline imediat următor va citi un șir vid — este cea mai frecventă eroare de execuție la subiectele cu fișiere.
Oprirea citirii se face pe condiția de succes a operației, nu pe funcția de test a sfârșitului de fișier apelată înainte de citire. Motivul: indicatorul de sfârșit de fișier se activează abia după o încercare de citire eșuată, deci un ciclu care testează sfârșitul înainte de citire prelucrează ultima valoare de două ori. Forma corectă pune citirea chiar în condiția ciclului.
La scriere, valorile trebuie separate explicit prin spațiu sau prin trecere la linie nouă; altfel 12 și 5 se lipesc în fișier și devin la recitire numărul 125.
Actualizarea unui fișier text
Întrebarea de teorie care sperie: cum modifici o valoare dintr-un fișier text? Răspunsul corect este că, în general, nu poți modifica pe loc. Într-un fișier text liniile au lungimi diferite, iar dacă înlocuiești un număr de două cifre cu unul de trei, nu ai unde să pui cifra suplimentară — ai suprascrie caracterele următoare.
Tehnica standard de actualizare, cerută la subiecte, are trei pași:
- se deschide fișierul original pentru citire și un fișier temporar pentru scriere;
- se parcurge fișierul original înregistrare cu înregistrare; fiecare componentă se scrie în fișierul temporar, fie neschimbată, fie modificată, fie deloc (dacă trebuie ștearsă), iar componentele noi se inserează la locul lor;
- se închid ambele fișiere, se șterge originalul și fișierul temporar se redenumește cu numele originalului.
Aceeași schemă rezolvă toate operațiile de întreținere: ștergerea unei înregistrări (nu o copiezi), inserarea (o scrii înainte de cea în fața căreia trebuie să apară), modificarea (o scrii cu valorile noi).
Există și varianta „încarcă tot în memorie”: se citește fișierul într-un vector, se modifică vectorul, apoi se rescrie fișierul de la zero. Este mai simplă și perfect acceptabilă la BAC dacă enunțul garantează un număr maxim de valori, dar cade exact acolo unde fișierele își arată forța — la volume care nu încap în memorie.
Excepția de la regulă merită reținută: la un fișier binar cu înregistrări de lungime fixă, actualizarea pe loc este posibilă, pentru că înregistrarea nouă ocupă exact spațiul celei vechi.
Fișiere binare și prelucrarea înregistrărilor
Un fișier binar conține imaginea exactă a datelor din memorie. Se deschide cu modul binary adăugat la modul de acces, iar transferul nu se mai face cu operatorii de citire/scriere formatată, ci cu metodele read și write, care primesc adresa zonei de memorie și numărul de octeți transferați.
Avantajele față de text, așa cum se cer la comparație:
- spațiu mai mic — un întreg ocupă un număr fix de octeți, indiferent de câte cifre are;
- viteză mai mare — dispare conversia dintre caractere și valori numerice;
- fidelitate — un număr real nu pierde zecimale din cauza formatării la scriere;
- acces direct posibil, dacă înregistrările au aceeași dimensiune.
Dezavantajele: fișierul nu este lizibil într-un editor de text și nu este portabil între arhitecturi care reprezintă diferit numerele.
O înregistrare este unitatea logică de informație, de regulă o structură cu mai multe câmpuri — de exemplu un elev cu nume, clasă și medie. Fișierul binar de înregistrări se prelucrează cel mai natural: se citește câte o structură întreagă dintr-o singură operație, se prelucrează, se trece la următoarea.
Atenție la capcana cea mai serioasă: o structură care conține pointeri (de exemplu un șir alocat dinamic) nu se poate salva direct în fișier binar. S-ar scrie adresa, nu conținutul, iar la o rulare ulterioară adresa nu mai înseamnă nimic. Se salvează doar structuri cu câmpuri de dimensiune fixă, tablourile de caractere fiind soluția uzuală în locul șirurilor dinamice.
Acces secvențial și acces direct; poziționarea în fișier
Fiecare flux deschis are un indicator de poziție — un „cursor” care arată de unde se va face următoarea operație. La accesul secvențial, cursorul înaintează singur, cu numărul de octeți citiți sau scriși; pentru a ajunge la a n-a componentă trebuie citite toate cele n-1 dinaintea ei.
La accesul direct, cursorul se mută explicit, cu metodele de poziționare: seekg pentru fluxul de intrare și seekp pentru cel de ieșire. Poziția se dă în octeți, măsurați față de un reper: începutul fișierului, poziția curentă sau sfârșitul fișierului. Poziția curentă se află cu tellg, respectiv tellp.
Formula cheie, care apare în aproape orice problemă de acces direct: dacă înregistrările au toate dimensiunea d, atunci înregistrarea cu numărul de ordine k, numerotată de la 0, începe la octetul *k d. Din același raționament rezultă și numărul total de înregistrări: te poziționezi la sfârșitul fișierului, citești poziția (adică dimensiunea în octeți) și o împarți la d**.
Greșeli de punctaj legate de poziționare:
- se calculează poziția în înregistrări, nu în octeți — cursorul ajunge în mijlocul primei înregistrări și se citește un amestec de câmpuri;
- se numerotează înregistrările de la 1 și se sare la (k d) în loc de ((k-1) d) — se citește vecina;
- se folosește accesul direct pe un fișier text cu linii de lungimi diferite: nu există o formulă a poziției, pentru că nu există dimensiune constantă.
Un fișier binar cu înregistrări fixe permite deci ceea ce un fișier text nu permite: citirea, modificarea și rescrierea unei singure înregistrări, fără a atinge restul fișierului.
De reținut
- fișier
- colecție de date memorată pe un suport extern, identificată prin nume, cu dimensiune nedeclarată în prealabil și cu date care persistă după terminarea programului
- fișier text
- fișier în care informația este memorată ca succesiune de caractere organizată pe linii, lizibil într-un editor de text și accesibil practic doar secvențial
- fișier binar
- fișier în care datele sunt memorate în reprezentarea lor internă din memorie, mai compact și mai rapid, dar nelizibil într-un editor de text
- acces secvențial
- mod de acces în care ajungerea la componenta k presupune parcurgerea tuturor componentelor care o preced
- acces direct
- mod de acces în care se poate sări imediat la o componentă, calculându-i poziția; posibil doar la fișiere cu înregistrări de dimensiune fixă
- înregistrare
- unitatea logică de informație dintr-un fișier, de regulă o structură cu mai multe câmpuri, prelucrată ca un tot unitar
- indicator de poziție
- cursorul intern al fluxului care arată octetul de la care se face următoarea operație de citire sau de scriere
- actualizare prin fișier temporar
- tehnica de modificare a unui fișier text prin copierea lui, cu schimbările dorite, într-un fișier nou, urmată de ștergerea originalului și redenumirea copiei
- închiderea fișierului
- operația care eliberează resursele și golește zona tampon pe disc, garantând scrierea efectivă a ultimelor date
Greșeli frecvente
Greșit: Ciclul de citire testează sfârșitul de fișier înainte de a citi
Corect: Indicatorul de sfârșit de fișier se activează abia după o citire eșuată, deci ultima valoare este prelucrată de două ori; citirea trebuie pusă chiar în condiția ciclului
Greșit: Se încearcă modificarea pe loc a unei valori dintr-un fișier text
Corect: Liniile unui fișier text au lungimi variabile; actualizarea se face prin rescrierea într-un fișier temporar, apoi ștergerea originalului și redenumirea copiei
Greșit: La accesul direct, poziția se calculează în număr de înregistrări, nu în octeți
Corect: Poziționarea se face în octeți: înregistrarea k, numerotată de la 0, începe la octetul k înmulțit cu dimensiunea unei înregistrări
Greșit: Se salvează într-un fișier binar structuri care conțin pointeri
Corect: Se scrie adresa, nu conținutul, iar la o rulare ulterioară adresa nu mai are sens; se folosesc câmpuri de dimensiune fixă, de exemplu tablouri de caractere
Greșit: Se scriu valori numerice în fișier text fără separator între ele
Corect: Fără spațiu sau trecere la linie nouă, 12 și 5 devin la recitire numărul 125; separatorul face parte din formatul fișierului text
Test — 6 întrebări ca la examen
1. Care este principala deosebire dintre un fișier și un tablou unidimensional?
- fișierul poate conține doar numere întregi
- datele din fișier sunt persistente, iar dimensiunea lui nu se declară în prealabil
- tabloul poate fi parcurs, iar fișierul nu
- fișierul se încarcă automat în memorie la pornirea programului
Vezi răspunsul
datele din fișier sunt persistente, iar dimensiunea lui nu se declară în prealabil. Fișierul păstrează datele după terminarea programului și nu are dimensiune fixată la compilare, spre deosebire de tablou. A treia variantă e falsă: fișierul se parcurge, doar că secvențial, iar prelucrarea lui e chiar scopul capitolului.
2. Un fișier existent, plin cu date, este deschis pentru scriere în mod obișnuit (fără mod de adăugare). Ce se întâmplă cu conținutul lui?
- rămâne neschimbat, iar scrierea începe de la sfârșit
- conținutul este golit (trunchiat)
- deschiderea eșuează, pentru că fișierul există deja
- conținutul este copiat automat într-un fișier de rezervă
Vezi răspunsul
conținutul este golit (trunchiat). Deschiderea pentru scriere trunchiază fișierul existent; datele vechi se pierd. Prima variantă descrie modul append, care trebuie cerut explicit — de aceea este distractorul care înșală cel mai des.
3. De ce un fișier text nu permite, în general, acces direct la componenta cu numărul k?
- pentru că fișierele text sunt protejate la scriere
- pentru că liniile au lungimi diferite, deci poziția componentei nu se poate calcula
- pentru că indicatorul de poziție nu există la fișierele text
- pentru că fișierele text nu pot depăși o anumită dimensiune
Vezi răspunsul
pentru că liniile au lungimi diferite, deci poziția componentei nu se poate calcula. Accesul direct presupune o formulă a poziției, care există doar dacă toate componentele au aceeași dimensiune. A treia variantă e falsă: indicatorul de poziție există la orice flux, dar fără dimensiune constantă nu poate fi calculat unde să sară.
4. Într-un fișier binar, fiecare înregistrare ocupă 40 de octeți. La ce octet începe înregistrarea cu numărul de ordine 7, dacă numerotarea începe de la 0?
- la octetul 7
- la octetul 240
- la octetul 280
- la octetul 320
Vezi răspunsul
la octetul 280. Poziția este 7 înmulțit cu 40, adică 280, pentru că înaintea înregistrării 7 se află 7 înregistrări complete. Varianta 240 apare când se numără greșit de la 1 și se calculează (7-1) înmulțit cu 40, iar 320 corespunde înregistrării următoare.
5. Cum se determină numărul de înregistrări dintr-un fișier binar cu înregistrări de dimensiune fixă, fără a-l parcurge?
- se citește prima înregistrare, care conține numărul total
- se poziționează cursorul la sfârșit, se citește poziția și se împarte la dimensiunea unei înregistrări
- se numără caracterele de sfârșit de linie
- nu se poate determina decât citind toate înregistrările
Vezi răspunsul
se poziționează cursorul la sfârșit, se citește poziția și se împarte la dimensiunea unei înregistrări. Poziția cursorului la sfârșitul fișierului este chiar dimensiunea lui în octeți, iar împărțirea la dimensiunea unei înregistrări dă numărul lor. Numărarea marcajelor de linie este specifică fișierelor text și nu are sens într-un fișier binar.
6. Dintr-un fișier text care conține pe fiecare linie un nume, trebuie ștearsă a treia linie. Care este metoda corectă?
- se poziționează cursorul la începutul liniei a treia și se scriu spații peste ea
- se copiază toate liniile, mai puțin a treia, într-un fișier temporar, apoi se șterge originalul și se redenumește copia
- se deschide fișierul în mod adăugare și se scrie o linie goală la final
- se micșorează dimensiunea fișierului cu lungimea liniei a treia
Vezi răspunsul
se copiază toate liniile, mai puțin a treia, într-un fișier temporar, apoi se șterge originalul și se redenumește copia. Ștergerea într-un fișier text se face rescriind conținutul dorit într-un fișier temporar, care apoi înlocuiește originalul. Prima variantă pare eficientă, dar suprascrierea cu spații nu elimină linia — ea rămâne în fișier, doar golită de conținut.
Deschide varianta interactivă — cu AI care îți explică