CORSO DI PROGRAMMAZIONE II Lezione 3 prof. E. Burattini a.a. 2009-2010
numeri[4] lettere[5] 2 3 1 6 A B G D K /* Esempio di lettura, confronto e scrittura in un array contenuto in un record a sua volta contenuto in un array di record*/ struct Tdati { int numeri[4]; char lettere[5]; }; Tdati dati[30]; numeri[4] lettere[5] 2 3 1 6 A B G D K dati[30]
/* Esempio di lettura, confronto e scrittura in un array contenuto in un record a sua volta contenuto in un array di record*/ struct Tdati { char lettere[20]; int numeri[4]; }; Tdati dati[30]; // PROTOTIPI void Inserimento(Tdati [], int); void Stampa(const Tdati [], int); void confronta(Tdati [], int); int elemArray=4; int main () { cout<<"\nCarica interi per riga "; Inserimento (dati, elemArray); Stampa(dati, elemArray); confronta(dati, elemArray); system("pause"); } void Inserimento (Tdati dat1[], int ndat) { for (int j=0;j<ndat;j++) { cout<<endl; for (int i=0;i<ndat;i++) {cout<<" A["<<j<<"]= ";cin>>dat1[j].numeri[i]; } cout << "lettere ="; cin >> dat1[j].lettere; }
/* Esempio di lettura, confronto e scrittura in un array contenuto in un record a sua volta contenuto in un array di record*/ struct Tdati { char lettere[20]; int numeri[4]; }; Tdati dati[30]; // PROTOTIPI void Inserimento(Tdati [], int); void Stampa(const Tdati [], int); void confronta(Tdati [], int); int elemArray=4; int main () { cout<<"\nCarica interi per riga "; Inserimento (dati, elemArray); Stampa(dati, elemArray); confronta(dati, elemArray); system("pause"); } void Stampa(const Tdati dat1[], int ndat) { for (int j=0;j<ndat;j++) {cout<<"\n riga "<<j<<--"<<dat1[j].lettere<<" ** "; for (int i=0;i<ndat;i++) { cout<<dat1[j].numeri[i]<<" "; } }
void confronta(Tdati dat1[], int ndat) { char lettere2[20]; int ass[ndat]; bool flag; int cont=0; cout <<"\n\n Lista numeri da confrontare "<<endl; for (int y=0;y<ndat;y++) cin>>ass[y]; cout<<"\n\nlettere da confrontare ";cin>>lettere2; for (int j=0;j<ndat;j++) { if (strcmp(dat1[j].lettere,lettere2)==0) cout<<"\n La lettere in posizione "<<j<<" coincide con quella assegnata"<<endl; else cont++; flag=true; int i=0; while ((i<ndat)&&(flag==true)) { if (dat1[j].numeri[i]!=ass[i])flag=false; i++; } if (flag==true) cout<<" riga "<<j<<" numeri uguali "<<endl; if (cont==ndat)cout<<" non ci sono stringhe uguali "<<endl; } if (flag==true) cout<<" non ci sono sequenze di numeri uguali "<<endl; int main () { cout<<"\nCarica interi per riga "; Inserimento (dati, elemArray); Stampa(dati, elemArray); confronta(dati, elemArray); system("pause"); } array rec1
Esercizi 1) Utilizzando la struttura Tpersona, definita nell’esempio aggiungere al menù la voce cancellazione di un record: la function da scrivere deve prima ricercare l’elemento da cancellare (con la funzione Ricerca) e, successivamente, cancellarlo dall’array di record . 2) Definire la struttura impiegato che, oltre a tutti i dati già contenuti in Tpersona, ha: data di assunzione; livello ( operaio, impiegato, dirigente, ….); stipendio annuo percepito l’anno precedente. Utilizzando il programma proposto nell’esempio, apportare le necessarie modifiche per gestire i dati della struttura impiegato ( inserimento, ricerca, stampa ).
Esercizi 3) Utilizzando la struttura Tpdata già definita, scrivere una procedura, SommaData che, assegnata una data ed un numero di giorni, restituisca, in formato gg mm aa, la somma tra la data ed il numero di giorni (si supponga che i mesi siano tutti di 31gg). Ad esempio SommaData applicata alla data 13 5 11 e 124 giorni deve restituire la data 13 9 11
Esercizi 4) Definire una struttura Orario che comprenda giorni, ore ( da 0 a 23 ), minuti e secondi rappresentato nel formato seguente: gg hh mm ss. Scrivere due procedure, SommaTempo e SottraiTempo, che, assegnati due tempi espressi in termini di numero di giorni, ore, minuti ed secondi, restituiscano, in formato coerente, rispettivamente la somma e la sottrazione tra i due tempi (sottraendo il tempo più lungo da quello più breve). Ad esempio SommaTempo applicata all’orario hh 13 mm 45 ss 23 e 224 secondi deve restituire l’orario hh 13 mm 49 ss7 Altro esempio SommaTempo applicata all’orario gg 2 hh 48 mm 10 ss 70 sommato a hh 23 mm 48 ss 50 Restituisce gg 5 hh 0 mm 0 ss 0 Mentre SottraiTempo Restituisce gg 3 hh 0 mm 22 ss 20 . orario2
FILE BINARI
Un accenno alla programmazione a oggetti. Oggetti: persone, animali, piante, automobili, aerei, palazzi, computer e quant'altro. Gli uomini pensano in termini di oggetti. Possiamo suddividere gli oggetti in due grandi categorie: oggetti animati e inanimati. I primi sono vivi, si muovono e intraprendono azioni, al contrario dei secondi. Entrambi i tipi di oggetti hanno una caratteristica in comune: gli attributi, come la dimensione, la forma, il colore e il peso. Hanno tutti dei comportamenti: una palla rotola, rimbalza, si gonfia o si sgonfia; un bambino piange, dorme, gattona, cammina e sbatte le palpebre; un'automobile accelera, frena e svolta; un asciugamano assorbe l'acqua. Noi studieremo i tipi di attributi e i comportamenti che caratterizzano gli oggetti software.
La classe è un tipo definito dall’utente con annesse tutte le operazioni e le proprietà che lo rendono adatto ai nostri scopi. Gli oggetti sono le variabili di quel tipo: diremo anche che un oggetto è un’istanza della classe. Per conservare la modularità, è necessario che ogni oggetto esegua il suo compito senza che l’utilizzatore conosca esattamente come ciò avvenga. Questo si verifica sicuramente con i tipi standard, int e float: infatti noi eseguiamo tutte le operazioni del caso senza necessariamente sapere come esse sono implementate.
In C l'unità di programmazione è la funzione. In C++ l’unità è la classe, da cui eventualmente si istanziano (creano) gli oggetti. La relazione fra classi e oggetti può essere esemplificata nel modo seguente: il progetto di un’auto sta all’automobile allo stesso modo in cui la classe sta ad un oggetto. Infatti, è possibile costruire parecchie automobili partendo dallo stesso progetto e, allo stesso modo, possiamo istanziare parecchi oggetti da una sola classe. Non si può correre, frenare o girare con l’auto di un progetto : si può solo correre, frenare o girare con un’auto vera.
Le strutture, utilizzabili con la parola chiave struct, hanno permesso di definire vari tipi, come studente, anagrafe, data etc., che si servono sia di tipi elementari che di altre strutture. Così come accade per i tipi int e float, sarebbe auspicabile poter definire delle operazioni anche per strutture tipo la struct studente. In questo caso, per esempio, potremmo avere, come operazioni la lettura e scrittura dei dati dello studente, il calcolo della media dei voti e delle tasse pagate fino ad un certo periodo, e così via. È, quindi, evidente che è necessario introdurre un nuovo costrutto che inglobi in sè sia i dati che le operazioni. A tal fine si introduce un nuovo tipo utilizzando la parola chiave class.
La nozione di classe e oggetti Tipo: l’insieme dei valori che può assumere una variabile (semplice o strutturata) e l’insieme delle operazioni che possono applicarsi a tali valori Classe: la definizione di un tipo esteso che racchiude sia i valori che lo caratterizzano (proprietà o attributi), sia gli operatori che agiscono su quei valori (metodi) operatoreA operatoreB var Tipo1 Tipo2 Tipo3 classeA oggetto metodo1 prop1 metodo2 prop2 prop2
Vantaggi delle classi Incapsulamento dati classeA metodi Incapsulamento dati I metodi come unico punto di accesso (interfaccia) alle sue proprietà Lo stato di un oggetto (l’insieme delle sue proprietà) può essere modificato solo attraverso l’interfaccia progettata (Oscuramento/Protezione dati) prop1 prop2 metodi prop3 prop4
Gli oggetti stream Uno stream è un'astrazione, che rappresenta un "qualcosa" da o verso cui "fluisce" una sequenza di bytes può essere interpretato come un "file intelligente" (con proprietà e metodi), che agisce come "sorgente" da cui estrarre (input), o "destinazione" in cui inserire (output) i dati Esempi di stream: Input da tastiera, output a schermo File in lettura, file in scrittura Buffer Canali di trasmissione di rete STREAM
Gli oggetti stream Un concetto importante è la posizione corrente in uno stream (file position indicator), che coincide con l'indice del prossimo byte che deve essere letto o scritto Ogni operazione di I/O modifica la posizione corrente La posizione corrente può essere ricavata o modificata direttamente usando particolari metodi STREAM c i a o
Gli oggetti stream Gli oggetti stream sono organizzati secondo una gerarchia di classi dette genericamente stream Ogni classe è specializzata per tipo di operatori di input e output supportati e per il tipo delle sorgenti e destinazioni (file, I/O, stringhe, etc.) <ofstream> ios ios_base istream ostream cin cout ifstream iostream ofstream <iostream> <ifstream> <fstream> fstream
Gli oggetti stream cin e cout sono oggetti (globali) rispettivamente delle classi istream e ostream di cui conosciamo già alcuni metodi: cin>>dato; (operatore di estrazione) cin.getline(char s[], int n, char c=’\n’) cout<<dato; (operatore di inserimento) cout.width(ampiezza); cout.precision(cifre); ios_base ios <iostream> cin istream cout ostream L’operatore punto (.) serve a selezionare un metodo di un oggetto <fstream> ifstream iostream ofstream fstream
File1.open(Disney, modalità di accesso) I file binari Abbiamo ora la necessità di conservare i dati sul disco per non doverli riscrivere ogni volta che il programma viene eseguito. Per gestire le strutture in maniera diretta, introduciamo i file binari. Ricordiamo che il nome fisico di un file è il nome del file sul disco come è visto dal sistema operativo; il nome logico, invece, è il nome con cui viene gestito dal programma. Il legame tra file fisico e file logico è dato dal metodo open applicato all’oggetto file: File1.open(Disney, modalità di accesso) File1=nome file logico Disney=nome file fisico (es.”pippo.dat”)
La classe stream ( flusso ) è la base per l’I/O in C++. Come precedentemente accennato essa è formata da una sequenza di byte che fluiscono da un dispositivo all’altro, da cui il termine stream. La sequenza di byte può fluire dalla tastiera alla memoria centrale del computer: abbiamo così l’input standard, l’oggetto cin, che è un oggetto stream. Analogamente, l’oggetto cout rappresenta l’output standard, il flusso di dati dalla memoria centrale al monitor. Agli oggetti cout e cin abbiamo applicato, rispettivamente, gli operatori di inserimento << e di estrazione >>, un po’ come accade ai numeri interi di tipo int a cui applichiamo gli operatori di incremento (++) e decremento (--).
Questi oggetti possono essere gestiti da altri metodi (procedure e funzioni) cui si accede mediante il punto (.), che rappresenta la selezione di un metodo dell’oggetto; per esempio, per leggere una stringa s dalla tastiera scriviamo cin.getline(s,100); dove cin è l’oggetto, getline il metodo, s la variabile stringa letta dalla tastiera; il punto che separa cin da getline indica che quel metodo appartiene a quell’oggetto.
Oltre agli oggetti cin e cout, il file <iostream> contiene anche l’oggetto cerr ; cerr è associato al dispositivo di errore standard per cui, se si verifica un errore di I/O, esso viene immediatamente notificato all’output standard (vedi esempio nel paragrafo successivo). Anche i file sono dei flussi e quindi appartengono alla classe stream; la libreria per la loro gestione è contenuta in <ifstream>, per quanto concerne l’input ed in <ofstream> per l’output; se si ha la necessità di eseguire funzioni di lettura e scrittura si può includere il file <fstream> che le contiene entrambe.
Una volta creata la corrispondenza tra file fisico e file logico, il sistema operativo si occuperà di travasare i dati dall’uno all’altro. Quando ordiniamo di scrivere sul file logico, i dati inviati vengono memorizzati in una zona di memoria temporanea connessa al file stesso. Nel momento in cui tale zona di memoria viene riempita oppure il file viene chiuso, allora il computer provvederà ad inviare tutti i dati al file fisico. Analogamente, nella fase di lettura, il computer legge dal file fisico una quantità di dati tale da riempire il buffer (oppure legge tutti i dati del file se questi sono in quantità inferiore alla lunghezza del buffer); ogni istruzione di lettura successiva continuerà a leggere dati dal buffer finché esso non si svuota; a quel punto i nuovi dati vengono prelevati dal file ed inseriti nel buffer per essere letti successivamente.
LA SINTASSI DEI FILE BINARI File1.open(nome file fisico, modalità di accesso) dove File1 è una variabile di tipo fstream , nome file fisico è il nome, comprensivo del path, con cui è conosciuto dal sistema operativo, modalità di accesso può assumere uno dei seguenti valori . Modalità di accesso Descrizione ios::in Apre il file per la lettura ios::out Apre il file per la scrittura ios::ate Apre il file posizionandosi alla fine del file. ios::app L’output viene aggiunto alla fine del file ios::trunc Se il file esiste viene cancellato scrivendoci sopra ios::binary Accesso in modalità binaria
ifstream, ofstream, fstream, ……. La classe ios è la capostipite di tutte le classi di I/O. Tutte le altre classi di I/O come ifstream, ofstream, fstream, ……. derivano da essa. Per poter utilizzare questa proprietà è necessario servirsi del namespace della classe, ios, seguito dall’operatore di scope:: per cui, ad esempio, la scrittura ios::in indica che il file sarà aperto soltanto in lettura, che è la modalità standard per i file dichiarati come ifstream. Si possono combinare varie modalità di accesso con l’operatore | (OR ) tra bit. Per esempio, la modalità di default di apertura di un file in scrittura è ios::out | ios::trunc perché in un file di tipo ofstream si può scrivere soltanto dopo aver cancellato tutto in quanto il puntatore di scrittura si dispone ad inizio file.
Oltre le classi ifstream e ofstream esiste la classe fstream che consente di aprire un file sia in lettura che in scrittura; alcuni compilatori aprono un file di tipo fstream con le seguenti modalità di default ios::in | ios::out I file di testo, sono gestiti in maniera sequenziale: per ricercare un dato è necessario scorrere tutte le celle iniziando dalla prima, mentre l’inserimento di un dato può avvenire soltanto alla fine. .
Il C++ gestisce anche i file ad accesso diretto in cui è possibile posizionarsi in qualsiasi punto all’interno di essi. Per questo tipo di file sono necessari due puntatori, uno che indica la posizione di lettura ed un altro quello di scrittura. Ci sono inoltre funzioni che consentono di leggere e scrivere in una qualsiasi posizione all’interno del file. Questi file, che hanno una struttura profondamente diversa dai file di testo, sono detti file binari.
I file binari possiedono due puntatori, uno in lettura ed uno in scrittura, che si posizionano esattamente nel punto successivo in cui va effettuata l’operazione; il puntatore di lettura punta al prossimo elemento da leggere, il puntatore di scrittura punta alla locazione in cui va scritto il successivo elemento. Quanto detto vale non solo per i file ma per qualsiasi stream, per cui si utilizzano spesso i termini get stream pointer per il puntatore di lettura e put stream pointer per quello di scrittura. Per questa ragione, le funzioni che leggono i dati hanno un carattere g(et) alla fine, mentre le funzioni di scrittura hanno una lettera p(ut).
Le funzioni da utilizzare per posizionare il puntatore all’interno del file binario sono le seguenti: Funzioni (metodi) Descrizione tellg() Questo metodo non richiede parametri; ritorna un intero che rappresenta la posizione del puntatore di lettura tellp() Anche questo metodo non richiede parametri; ritorna un intero che rappresenta la posizione del puntatore di scrittura seekg(long pos) Questo metodo posiziona il puntatore di lettura nella posizione pos Ricordiamo che la posizione parte dal valore 0 seekp(long pos) Questo metodo posiziona il puntatore di scrittura nella posizione pos
seekg( long pos, direzione) seekp( long pos, direzione) I due metodi, seekg e seekp, possono essere sovraccaricati con altre due funzioni che stabiliscono anche la direzione in cui muovere il puntatore: seekg( long pos, direzione) seekp( long pos, direzione) dove direzione può assumere uno dei valori seguenti . Direzione Descrizione ios::beg Il valore del parametro pos è calcolato dall’inizio del file ( è la condizione di default ) ios::cur Il valore del parametro pos è calcolato dalla posizione corrente ios::end Il valore del parametro pos è calcolato dalla fine del file e quindi, se si vuole leggere, deve essere un numero negativo