Programarea Calculatoarelor, seria CC
Până în acest moment, când am folosit expresiile "scriere din fișier" sau "citire din fișier", ne-am referit în mod exclusiv la fișiere de tip text.
Practic,
Spre deosebire de ele,
Ca de obicei, ambele metode de stocare au avantaje și dezavantaje care indică folosirea uneia sau a celeilalte în funcție de aplicație:
Pentru că o imagine valorează cât o mie de cuvinte, vom ilustra grafic cum are loc reprezentarea efectivă în memorie a informațiilor în format text și binar. Fie următoarea structură de date care conține informații despre un elev.
typedef struct Elev{
short int nota1;
short int nota2;
char nume[10];
float medie;
} Elev;
[ ... main() și alte funcții ...]
Elev elev;
elev.nota1 = 7;
elev.nota2 = 10;
elev.medie = (elev.nota1 + elev.nota2) / 2.0;
strcpy(elev.nume, "Cartman");
Sa considerăm că avem două fișiere:
Primul lucru pe care trebuie să îl facem pentru a putea folosi un fișier este să îl deschidem. În acest sens, lucrurile stau foarte simplu: trebuie doar să adaugăm "b" la șirul care specifică modul de deschidere al unui fișier în funcția
Câteva exemple:
Semnificație
Fișiere binare
Fișiere text
citire
"rb"
"r"
scriere
"wb"
"w"
adăugare
"ab"
"a"
Pentru citire la nivel de octet se foloseşte funcţia
Semnificaţia argumentelor este următoarea:
Pentru scriere la nivel de octet se foloseşte funcţia
Semnificaţia argumentelor este următoarea:
Nu în ultimul rând, fișierele binare au o proprietate interesantă: Am spus că spațiul ocupat de diverse articole depinde exclusiv de tipul lor de dată. Cu alte cuvinte, dacă am vrea să citim al 101-lea număr întreg dintr-un fișier binar care conține doar numere întregi, știm sigur că acest număr ocupă Bytes-ii cu numerele
Ar fi foarte convenabil dacă am putea sări peste restul fișierului direct la acea locație de memorie. În realitate, acest lucru este posibil. Pentru a rezolva această problemă, C-ul pune la dispoziție două funcții:
În aplicaţiile mari, în mod normal modulele diferite de program se implementează în fişiere separate, urmând a fi necesară compilarea executabilului final din mai multe surse. Includerea unui fişier sursă în alt fişier sursă se face cu ajutorul directivei de preprocesare
#ifndef __STDLIB__
#define __STDLIB__
#include <stdlib.h>
#endif /* __STDLIB__ */
Înainte de a se include pentru prima dată
Să se definească o structură cu următoarele date despre un produs:
Să se implementeze definiția structurii într-un fișier numit
Funcția main va primi argumente din linia de comandă și va trebui să importe tipul de dată
ATENŢIE! Definiţia tipului de date, includerea bibliotecilor precum şi antetele funcţiilor care se vor defini ulterior vor fi grupate într-un fişier header!
Scrieți o funcție pentru crearea unui fişier binar care să conţină 100 de produse cu date generate aleator astfel:
Hints:
Scrieți o funcție pentru afişarea pe monitor a fişierului creat anterior, câte un articol pe o linie.
Scrieți o funcție pentru sortarea articolelor din fişier crescător după nume.
Hint-uri:
Scrieți o funcție pentru citirea unui număr întreg
Dacă nu există un produs cu acel nume atunci se afişează un mesaj corespunzător.
Hint:
Completați următorul schelet de cod astfel încât să realizeze salvarea și apoi restaurarea a 4 structuri care contin siruri alocate dinamic. În fișierul binar NU aveți voie să scrieți și terminatorul de șir (caracterul
Procesul de salvare al unui obiect într-un fișier este uneori mai complicat decât o simplă copiere. Dacă obiectul conține membrii alocați dinamic, atunci nu mai este suficientă o scriere de tip "shallow" (simplă copiere), deoarece se pierde informația din obiectele alocate dinamic.
În concluzie, în astfel de cazuri, trebuie să "aplatizăm" structura, adică să scriem în fișier absolut toate datele referențiate de aceasta prin pointeri, iar la restaurare să realocăm toată memoria și să refacem pointerii. Procesul de salvare al unei structuri în formă binară se numește serializare, iar reconstruirea acestuia din formă binară se numește deserializare.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 4
typedef struct Persoana{
char* nume;
char* prenume;
} Persoana;
void serializeaza(Persoana v[], int nrPersoane, char* fileName)
{
FILE* f = fopen(fileName, "wb");
//TODO! Adauga codul de serializare: ~10 linii
fclose(f);
}
void deserializeaza(Persoana v[], int nrPersoane, char* fileName)
{
FILE* f = fopen(fileName, "rb");
//TODO! Adauga codul de deserializare: ~10 linii
fclose(f);
}
int main()
{
Persoana v[N],w[N];
char* prenume[N] = { "Eric", "Kyle", "Stan", "Kenny" };
char* nume[N] = { "Cartman", "Broflovski", "Marsh", "McCormick" };
int i;
for (i = 0; i < N; i++){
v[i].nume = nume[i];
v[i].prenume = prenume[i];
}
// Serializam vectorul intr-un fisier
serializeaza(v, N, "persoane.bin");
// Deserializam in alt vector, din acelasi fisier. Ar trebui sa obtinem aceleasi informatii.
deserializeaza(w, N, "persoane.bin");
for (i = 0; i < N; i++){
printf("%s %s\n",w[i].prenume,w[i].nume);
}
return 0;
}