1
Esempi d’uso di file binari.
Scriviamo un semplice programma che calcola la lunghezza di un file di
qualsiasi natura.
I commenti all’interno del codice forniscono ulteriori delucidazioni.
#include <iostream>
#include <cstdlib>
#include <fstream>
using namespace std;
int main () {
char Nomefile[80];
double lun;
//il file è di sola lettura
ifstream file;
cout << "Nome File=";
//se il file non è nella stessa directory fornire
il path completo
cin >> Nomefile;
//file binario aperto in input
file.open(Nomefile,ios::in|ios::binary);
// se il file non esiste il programma termina
if (!file) {
cerr<<"Non si puo' aprire il file"<<endl;
system("pause");
return -1;
}
//il puntatore di lettura va alla fine del file
file.seekg(0,ios::end);
// legge la posizione del puntatore di lettura
lun=file.tellg();
// chiude il file
file.close();
cout<<"Lunghezza "<<Nomefile<<
" ="<<lun<<endl;
system("pause");
return 0;
}
2
Ci sono ancora due metodi che ci consentono di scrivere e leggere sui
file binari.
I loro prototipi sono
write ( (char*) indirizzo-di-blocco-memoria, int grandezza-blocco)
read ( (char*) indirizzo-di-blocco-memoria, int grandezza-blocco)
Questi due metodi, abbastanza simili, gestiscono soltanto blocchi di
memoria che, attraverso il casting, vengono trasformati in caratteri;
il carattere * dopo il char, sta ad indicare che è necessario fornire
soltanto l’indirizzo del primo carattere come primo parametro e la
lunghezza di tutto il blocco come secondo parametro.
.
3
Ad esempio per costruire un file di date, bisogna operare in questo modo:
•definire un file di tipo fstream di nome
fstream datafile;
•aprire il file datafile sia in input che in output ed in formato binario :
datafile.open(NomeFileFisico,ios::in|ios::out|ios::binary);
•determinare la lunghezza del record Tpdata con l’istruzione sizeof:
lunghezza=sizeof(Tpdata);
•definire almeno una variabile di tipo Tpdata:
Tpdata data1;
•scrivere i dati posizionandosi nel punto voluto con l’istruzione seekp(……) e dare
il comando:
write( (char*) &data1, lunghezza);
•leggere i dati posizionandosi nel punto voluto con l’istruzione
seekg(……)
•dare il comando:
read( (char*) &data1, lunghezza);
Se il file è di tipo ofstream si può utilizzare soltanto il metodo
write(……………),
se è di tipo ifstream si può solo leggere con il metodo
read(…………).
4
Esempio
Riprendiamo l’esempio della lezione 2.
In questo caso il programma deve gestire un insieme di persone
conservando i dati sul disco; esso, inoltre, deve consentire
l’inserimento dei dati, la ricerca di un dato conoscendo il cognome
di una persona e la stampa su video di tutte le persone inserite o a
scelta ordinate per data di nascita.
Le function
int MenuScelta();
void Inserimento(Tpersona&, char&);
void Stampa(const Tpersona&);
restano inalterate.
L’unica function a cui dovremo apportare delle modifiche è la
function Ricerca.
5
.
Altre modifiche riguarderanno il main:
dobbiamo introdurre la dichiarazione di un file binario ed una
costante intera che rappresenti la lunghezza del record.
La prima volta che viene richiamato il programma, non trovando
alcun file fisico, il programma va in errore; nella gestione
dell’errore esso crea il file e, dopo, termina.
Tutte le esecuzioni successive non forniranno alcun errore di
apertura del file.
Riportiamo il programma principale senza le funzioni;
successivamente discuteremo la nuova versione della funzione
Ricerca.
.
6
.
// PROTOTIPI
int MenuScelta();
void Inserimento(Tpersona&,char&);
void Stampa(const Tpersona&);
bool Ricerca(char[],fstream&, Tpersona&, int,const int);
double dataNum(Tpdata x);
void ordinaBuble(Tpersona [] , int n);
void scambia (double&, double&);
7
Il main inizia con le dichiarazioni delle variabili
persona1 di tipo Tpersona,
corrente, che rappresenta l’indice corrente dell’array,
NumPers, che rappresenta il numero attuale di persone memorizzate.
scelta denota l’operazione da eseguire volta per volta,
ch e cognome2 sono le variabili di input delle function Inserimento e
Ricerca.
8
Il main contiene un ciclo do… while (il ciclo deve essere eseguito almeno una
volta) al cui interno appare l’istruzione switch.
caso 1 corrisponde all’inserimento dati; se l’utente preme ‘s’ i dati della
persona vengono aggiunti all’array Persone e la variabile NumPers
incrementata di uno;
caso 2 si assegna il cognome da ricercare e si richiama la funzione Ricerca: se il
valore restituito di corrente è maggiore di -1, si stampano tutti i dati, altrimenti
si avverte l’utente che il dato non è stato trovato;
caso 3 si esegue un ciclo for per stampare tutte le persone memorizzate.
caso 4 si esegue un ciclo for per stampare tutte le persone ordinate per data di
nascita.
9
La funzione MenuScelta deve scrivere sul monitor tutte le opzioni
disponibili dando all’utente la possibilità di inserire soltanto un
intero compreso tra 1 e 5. La funzione, dopo aver controllato che il
valore rispetti tali limiti, deve restituirlo al programma chiamante.
int MenuScelta() {
int sc;
do {
cout <<" GESTIONE PERSONE "<< endl;
cout <<" \n";
cout <<"1 - INSERIMENTO"<<endl;
cout <<"2 - RICERCA"<<endl;
cout <<"3 - STAMPA TUTTI"<<endl;
cout <<"4 - STAMPA DATI ORDINATI x NASCITA"<<endl;
cout <<"5 - FINE"<<endl;
cout <<" Scelta=";
cin >>sc;
} while (sc<1 || sc>5);
return sc;
10
}
La procedura Inserimento restituisce la struttura persona in
pers1 e la variabile carattere ch che può contenere il carattere ‘s’
o ‘n’. Le istruzioni della procedura si limitano ad acquisire i dati
da tastiera.
void Inserimento (Tpersona& pers1, char& ch)
{
cout << " INSERIMENTO PERSONE "<<endl;
cout << "Cognome =";
cin >> pers1.cognome;
cout << "Nome =";
cin >>pers1.nome;
cout << "Data di nascita GG MM AAAA =";
cin >> pers1.nascita.giorno>>pers1.nascita.mese >>pers1.nascita.anno;
cout <<"Luogo di nascita:";
cin >>pers1.luogo;
cout<<"Salva (s/n)=";
cin>>ch;
}
11
La procedura Stampa visualizza sul monitor la struttura persona
contenuta in pers1 con la clausola const ; essa ha lo scopo di non
consentire la variazione dei dati membri della struttura.
void Stampa(const Tpersona& pers1) {
cout << "Cognome :"<< pers1.cognome<< " Nome:"<<pers1.nome<<endl;
cout << "Luogo ="<<pers1.luogo << " Data di nascita :";
cout<<pers1.nascita.giorno<<'/'<<pers1.nascita.mese<<'/'<<pers1.nascita.anno;
cout<<endl;
}
12
#include <iostream>
#include <fstream>
using namespace std;
struct Tpdata {
int giorno;
int mese;
int anno;
};
struct Tpersona {
char cognome[30];
char nome[20];
Tpdata nascita;
char luogo[20];
};
Tpersona Persone[30];
const char Nomefile[]="persone.dat";
// PROTOTIPI
int MenuScelta();
void Inserimento(Tpersona&,char&);
void Stampa(const Tpersona&);
bool Ricerca(char[],fstream&, Tpersona&, int,const int);
.
13
int main () {
Tpersona persona1;
int corrente;
int NumPers;
int scelta;
char ch, cognome2[30];
const int Lrec=sizeof(Tpersona);
// lunghezza della struttura Tpersona
fstream filepers;
// definizione ed apertura alla fine del file
filepers.open(Nomefile,ios::in|ios::out|ios::binary|ios::ate);
// apre il file in lettura o scrittura, modalità binaria,
// posizionandosi alla fine
//se il file non esiste lo crea con l'istruzione seguente
if (!filepers) { filepers.open(Nomefile,ios::out|ios::binary|ios::trunc);
// apre il file in modalità binaria,
// posizionandosi in testa e cancellando quanto già c'è
return 1;
}
NumPers=filepers.tellg()/Lrec; //calcola il numero di persone contenute nel file
tellg()/ ritorna un intero che rappresenta la posizione del puntatore di lettura
14
do {
scelta=MenuScelta();
cout<<"Numero Persone = "<<NumPers<<endl;
switch (scelta)
{
posiziona il puntatore di scrittura nella
posizione pos
case 1:
Inserimento (persona1,ch);
if (ch=='s') {
filepers.seekp(0,ios::end); //si posiziona per la scrittura alla fine del file
filepers.write((char*) &persona1, Lrec);
//scrive sul file
NumPers++; //incrementa di uno il numero di persone inserite nel file
}
break;
case 2:
cout<<"\n Cognome da ricercare=";
cin>>cognome2;
if (Ricerca(cognome2, filepers, persona1, NumPers, Lrec))
Stampa(persona1);
else
cout<<"\n Dati non trovati"<<endl;
break;
15
La funzione, Ricerca, ricerca i dati di una persona. Il cognome inserito da
tastiera viene inviato alla funzione che, attraverso una ricerca lineare,
restituisce vero se la persona è stata trovata, falso nel caso in cui quel cognome
non esiste nel file.
bool Ricerca(char cognome2[], fstream &file, Tpersona &s1, int Num, const
int Lr)
{
int i=0;
file.seekg(0, ios::beg);
//posiziona lettura testa file
bool trovato=false;
while (i<Num && !trovato) {
file.read((char*) &s1, Lr);
//legge da file un singolo record
if (strcmp(s1.cognome,cognome2)==0) // confronta stringhe
{
trovato=true;
}
else i++;
}
return trovato;
16
}
case 3:
cout << "\n\n STAMPA DATI "<<endl;
filepers.seekg(0,ios::beg);
//si posizione in testa al file
for (int i=0; i<NumPers; i++) {
filepers.read((char*) &persona1, Lrec);
//legge i dati di un record
Stampa(persona1);
}
break;
case 4:
cout << "\n\n STAMPA DATI ORDINATI x NASCITA"<<endl;
filepers.seekg(0,ios::beg);
for (int i=0; i<NumPers; i++) {
filepers.read((char*) &persona1, Lrec);
//legge i dati di un record
Persone[i]=persona1;
}
ordinaBubble(Persone, NumPers);
for(corrente=0;corrente<NumPers;corrente++)
Stampa(Persone[corrente]);
break;
}
} while (scelta>0 && scelta<5);
filepers.close();
17
return 0;
void ordinaBubble (Tpersona vet[], const int N)
{
int j, k;
La procedura ordinaBubble ordina i dati
Tpdata nasc1,nasc2;
che preventivamente sono stati inseriti in
for (k=0; k<N-1; k++)
un array vet.
for (j=N-2; j>=k; j--)
{ nasc1=vet[j].nascita;
nasc2=vet[j+1].nascita;
if ( dataNum(nasc1) > dataNum(nasc2) )
scambia (vet[j],vet[j+1]);
}
}
void scambia (Tpersona &x1, Tpersona &x2)
{
Tpersona s;
s=x1;
x1=x2;
x2=s;
}
filebin2
double dataNum(Tpdata x)
{
return x.anno*10000+x.mese*100+x.giorno;18
}
case 4:
cout << "\n STAMPA DATI ORDINATI x NASCITA"<<endl;
filepers.seekg(0,ios::beg);
int j=0;
for (int i=0; i<NumPers; i++)
{ filepers.read((char*) &persona1, Lrec);
//legge i dati di un record
int k=0;
if (j==0) {Persone[0]=persona1;j++;}
else
{ k=j;Persone[k]=persona1;
while ((k>=0)&&
((dataNum(Persone[k].nascita)<dataNum(Persone[k-1].nascita))))
{
scambia(Persone[k],Persone[k-1]);
k--; }
j++;
}
}
for(corrente=0;corrente<NumPers;corrente++)
Stampa(Persone[corrente]);
19
Ordinamento con insertion-sort
break;
Esercizi.
1) Sia dato un file non ordinato Azioni.dat di record del tipo:
struct tipor {
char azione[20];
float valore_minimo;
float valore_massimo;
float valori_ultima_settimana[7];
};
Costruire un array di record chiamato Affari contenente tutti i record
riguardanti le azioni che hanno un valore medio, nell’ultima
settimana, maggiore o uguale dei due terzi del valore_massimo.
Ordinare i record dell’array Affari per valore_minimo e, a parità, per
nome azione.
20
2) Sia dato un file di articoli di magazzino, articoli.dat, contenente
record del tipo
struct TpArticolo {
int Codice;
char Descrizione[30];
int Quantità;
double Costo, Prezzo;
};
Scrivere una procedura che costruisca un array di record chiamato
Guadagni che conservi tutti gli articoli il cui guadagno complessivo
su ogni singolo pezzo è di almeno il 20%.
Tali articoli, ordinati per guadagno complessivo decrescente, devono
essere scritti sul file guadagni.dat.
N.B. Guadagno complessivo = (Prezzo - costo)* Quantità.
21
3) Consideriamo le strutture seguenti:
struct TpArt {
int Codice;
char Descrizione[30];
int Qmag: integer; //quantità contenuta in magazzino
float Prezzo;
int Scorta; //quantità minima in magazzino, oltre la quale si ordina altra merce
};
struct TpVend {
int Codice;
int Qvend;
};
Un grande magazzino ha degli articoli di tipo TpArt conservati nel file Articoli.bin; il
campo Qmag rappresenta la quantità di quell’articolo presente in magazzino, Scorta
la quantità minima necessaria per poter ordinare altri articoli dello stesso tipo.
Il file Vendite.bin contiene record del tipo TpVend che rappresentano la vendita
giornaliera degli articoli; il campo Codice è lo stesso di TpArt, mentre Qvend
rappresenta la quantità venduta. Entrambi i file sono ordinati per codice.
Scrivere una procedura che aggiorni il file Articoli.bin e stampi tutti gli articoli la cui
quantità è minore uguale al campo Scorta ordinati in maniera crescente per Scorta,
.
22
4) -
Si considerino le definizioni:
struct Tpdata{
int giorno;
int mese;
int anno;
}
struct Tpfattura{
int CodFor; //Codice Fornitore
Tpdata DataF; //data fattura
int numero; //numero fattura
double netto;
double iva;
double totale; //totale fattura
}
struct Tpfornitore{
int CodFor; //Codice Fornitore
char RagSoc[30]; //nome fornitore
char via[30];
char citta[30];
char provincia[2];
double TotAcq; //totale acquisti dal
fornitore
}
Una ditta acquista vari articoli dai fornitori contenuti nel file “fornitori.dat”. Il campo
TotAcq rappresenta il totale in euro degli acquisti effettuati presso il fornitore nei primi
tre trimestri dell’anno. Il file “fatture.dat” contiene, invece, le fatture di acquisto della
ditta emesse durante tutto l’ultimo trimestre nei confronti degli stessi fornitori.
Calcolare per ogni fornitore il totale degli acquisti effettuati dalla ditta. Se la cifra
supera i 100.000 euro, viene riconosciuto alla ditta un ulteriore sconto del 3% sul totale
degli acquisti .
Scrivere una procedura che scriva su un file “sconto.dat”
Nome fornitore, città, provincia, rimborso da richiedere
per tutti i fornitori della ditta, ordinati alfabeticamente per nome, a cui va richiesto lo
23
sconto.
Scarica

ppt