Fondamenti di Informatica - Esercizi

7 downloads 148 Views 34KB Size Report
zappa 1. 4.2 SOLUZIONI. Soluzione Esercizio 1. Si assume il vettore Cambio ValutaOdierna[10] come variabile globale data. Vengono risolti i punti 1 e 2, ...
Capitolo 4

Fondamenti di Informatica Esercizi Vengono proposti alcuni esercizi relativi alle funzioni ed ai file e vengono presentate le relative soluzioni.

1

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

2

4.1

FILE

} Esercizio 1 Sia dato il file binario "Incassi.dat" (il file gi`a esiste) contenente elementi con la seguente struttura: typedef struct { char Valuta; float Importo; } Entrata; Ogni record riporta l’importo di un’entrata nella relativa valuta (Dollaro, Sterlina, rappresentata tramite il carattere iniziale). Sia dato un vettore di 10 elementi Cambio ValutaOdierna[10] contenente record con la seguente struttura: typedef struct { char Valuta; float Valore; } Cambio; Per ogni valuta (Dollaro, Sterlina, ) c’`e un record di ValutaOdierna che riporta il Valore del cambio odierno rispetto all’Euro (cio`e il cambio odierno Euro/Dollaro, Euro/Sterlina, ). Scrivere un programma C che esegua le seguenti operazioni: 1. Calcolare il totale in Euro delle entrate memorizzate nel file "Incassi.dat", considerando i cambi riportati in ValutaOdierna (si suppone che tutte le valute presenti in "Incassi.dat" siano anche presenti in ValutaOdierna). 2. Modificare il file "Incassi.dat", incrementando del 10% tutte le entrate con valuta Dollaro. 3. Visualizzare il contenuto del file "Incassi.dat". Esempio: File "Incassi.dat" Valuta Importo D 490.76 S 630.4 F 30 D 332 Y 4432

Vettore ValutaOdierna Valuta Importo D 1.0388 S 0.6258 ... ... ... ...

File "Incassi.dat" dopo la modifica Valuta Importo D 539.836 S 630.4 F 30 D 365.2 Y 4432

TOTALE IN EURO 1862.8425

} Esercizio 2 Siano dati due file binari "Conta1.dat" e "Conta2.dat" (i file gi`a esistono) contenenti elementi con la seguente struttura:

4.1. FILE

3

typedef struct { char Parola[10]; int Count; } Registro; In entrambi i file gli elementi sono ordinati (alfabeticamente) sul campo Parola. In Parola ci sono solo caratteri minuscoli. Scrivere un programma C che esegua le seguenti operazioni: 1. Determinare quali sono le parole di "Conta1.dat" non contenute in "Conta2.dat". 2. Costruire un nuovo file ”ContaMer.dat” sempre ordinato sul campo Parola ed ottenuto per fusione dei due file "Conta1.dat" e "Conta2.dat" ; se una parola compare sia in "Conta1.dat" che in "Conta2.dat", tale parola comparir`a una sola volta in "ContaMer.dat" con il campo count dato dalla somma dei due count. File "Conta1.dat" Parola Count alba 4 albero 1 fase 3 fuoco 3 vetro 4

File "Conta2.dat" Parola Count albero 4 auto 1 fuoco 3 tasto 3 vetro 4 zappa 2

File "ContaMer.dat" Parola Count alba 4 albero 5 auto 1 fase 3 fuoco 6 tasto 3 vetro 8 zappa 2

} Esercizio 3 Sia dato il file binario "Conta.dat" (il file gi`a esiste) contenente elementi con la seguente struttura: typedef struct char Parola[10]; int Count; Registro; Gli elementi sono ordinati (alfabeticamente) sul campo Parola. In Parola ci sono solo caratteri minuscoli. Scrivere un programma C che esegua le seguenti operazioni: 1. Determinare qual’`e l’iniziale pi`u frequente nelle parole contenute in "Conta.dat" (ad esempio, nel file "Conta.dat" dato riportato in basso, l’iniziale pi`u frequente e` f’). 2. Acquisire da input N parole e modificare il file "Conta.dat": se la parola acquisita e` gi`a nel file, incrementare di uno il relativo campo Count, altrimenti inserire in ordine la parola con count=1. 3. Modificare il file "Conta.dat" considerando le parole contenute in un file di testo "testo.txt" , cio`e leggere il file "testo.txt" e per ciascuna parola in esso riportata, modificare il file "Conta.dat". Nel file "testo.txt" le parole sono separate da uno o pi`u spazi bianchi e dal carattere a-capo ’\n’.

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

4

File "Contadat" Parola Count alba 4 albero 1 fase 3 fuoco 3 vetro 4

4.2

Parole acquisite albero auto fuoco tasto auto zappa

File "Contadat" modificato Parola Count alba 4 albero 2 auto 2 fase 3 fuoco 4 tasto 1 vetro 4 zappa 1

SOLUZIONI

} Soluzione Esercizio 1 Si assume il vettore Cambio ValutaOdierna[10] come variabile globale data. Vengono risolti i punti 1 e 2, scrivendo una funzione. Domanda 1. void Domanda1(void){ FILE *FI; Entrata A; float Somma=0; if ((FI=fopen("Incassi.dat","rb"))==NULL) printf("non posso aprire il file %s", "Conta.dat"); else { while (fread(&A,sizeof(Entrata),1,FI)!=0) Somma= Somma + A.Importo/ValoreValutaOdierna(A.Valuta); printf("\n TOTALE IN EURO > %f ",Somma); fclose(FI); } } /* end Domanda1 */ float ValoreValutaOdierna(char V){ int I; for(I=0;I0) if (!RicercaSequenziale(NomeF2,R1.Parola))

6

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

printf("\n%s fclose(F1); } }

%d",R1.Parola,R1.Count);

/* restituisce 1 se trova Nome nel file 0 nel caso in cui non la trova o in cui non puo’ aprire il file */ int RicercaSequenziale(Stringa NomeF1, Stringa Nome){ FILE *F1; Registro R1; if ((F1=fopen(NomeF1,"rb"))!=NULL) { while ((fread(&R1,sizeof(Registro),1,F1)>0) && (StringaMinoreDi(R1.Parola,Nome))); fclose(F1); /* prima di uscire si chiude il file*/ if (StringaUgualeA(R1.Parola,Nome)) return 1; } return 0; } Domanda 2. Indichiamo i tre file Conta1.dat, Conta2.dat e ContaMer.dat, rispettivamente con NF1, NF2 e NFM. - apri NF1 in lettura, NF2 in lettura e NFM in scrittura - prendi il primo dato da NF1 in R1 - prendi il primo dato da NF2 in R1 - finche’ NF1 non e’ finito e NF2 non e’ finito - se R1 e’ uguale a R2 - somma al count di R1 quello di R2 - inserisci R1 in NFM - prendi il prossimo dato da NF1 in R1 - prendi il prossimo dato da NF2 in R2 altrimenti - se R1 e’ minore di R2 - inserisci R1 in NFM - prendi il prossimo dato da NF1 in R1 altrimenti - inserisci R2 in NFM - prendi il prossimo dato da NF2 in R2

4.2. SOLUZIONI

7

- trasferisci i restanti dati di NF1 in NFM - trasferisci i restanti dati di NF2 in NFM Traduciamo il controllo del ciclo di lettura dei due file (finch`e NF1 non e` finito e NF2 non e` finito) come abbiamo sempre fatto con un unico file while (fread(&R1,sizeof(Registro),1,F1)!=0 && fread(&R2,sizeof(Registro),1,F2)!=0){ ... In questo caso se in un certo passo si scrive sul file NFM il record R1 del file NF1, al passo successivo occorre considerare il record successivo di NF1 ma lo stesso record R2 di NF2: questo significa che bisogna rileggere di nuovo il record R2 di NF2, pertanto occorre effettuare fseek(F2,-1*sizeof(Registro),SEEK_CUR); Questa modalit e` utilizzata nella seguente funzione : void MergeFileUNO(Stringa NF1, Stringa NF2, Stringa NFM){ FILE *F1,*F2,*FM; Registro R1,R2; if ((F1=fopen(NF1,"rb"))!=NULL && (F2=fopen(NF2,"rb"))!=NULL && (FM=fopen(NFM,"wb"))!=NULL ) { while (fread(&R1,sizeof(Registro),1,F1)!=0 && fread(&R2,sizeof(Registro),1,F2)!=0){ if (StringaUgualeA(R1.Parola,R2.Parola)){ R1.Count=R1.Count+R2.Count; fwrite(&R1,sizeof(Registro),1,FM); fseek(F1,-1*sizeof(Registro),SEEK_CUR); fseek(F2,-1*sizeof(Registro),SEEK_CUR); } else if (StringaMinoreDi(R1.Parola,R2.Parola)){ fwrite(&R1,sizeof(Registro),1,FM); fseek(F2,-1*sizeof(Registro),SEEK_CUR); } else { fwrite(&R2,sizeof(Registro),1,FM); fseek(F1,-1*sizeof(Registro),SEEK_CUR); } } /* copia in FM di tutti quelli eventualmente rimasti in F1 */ while(fread(&R1,sizeof(Registro),1,F1)!=0) fwrite(&R1,sizeof(Registro),1,FM);

8

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

/* copia in FM di tutti quelli eventualmente rimasti in F2 */ while(fread(&R1,sizeof(Registro),1,F2)!=0) fwrite(&R1,sizeof(Registro),1,FM); fclose(F1); fclose(F2); fclose(FM); } } Con la precedente soluzione occorre rileggere pi`u volte lo stesso record; ci`o pu`o essere evitato utilizzando nel controllo del ciclo di lettura dei due file (finch`e NF1 non e` finito e NF2 non e` finito) la funzione feof file ... fread(&R1,sizeof(Registro),1,F1); fread(&R2,sizeof(Registro),1,F2); while (!feof(F1)&&!feof(F2)){ ... Questa modalit`a e` utilizzata nella seguente funzione : void MergeFileDUE(Stringa NF1, Stringa NF2, Stringa NFM){ FILE *F1,*F2,*FM; Registro R1,R2; if ((F1=fopen(NF1,"rb"))!=NULL && (F2=fopen(NF2,"rb"))!=NULL && (FM=fopen(NFM,"wb"))!=NULL ) { fread(&R1,sizeof(Registro),1,F1); fread(&R2,sizeof(Registro),1,F2); while (!feof(F1)&&!feof(F2)){ if (StringaUgualeA(R1.Parola,R2.Parola)){ R1.Count=R1.Count+R2.Count; fwrite(&R1,sizeof(Registro),1,FM); fread(&R1,sizeof(Registro),1,F1); fread(&R2,sizeof(Registro),1,F2); } else if (StringaMinoreDi(R1.Parola,R2.Parola)){ fwrite(&R1,sizeof(Registro),1,FM); fread(&R1,sizeof(Registro),1,F1); } else {

4.2. SOLUZIONI

9

fwrite(&R2,sizeof(Registro),1,FM); fread(&R2,sizeof(Registro),1,F2); } } /* copia in FM di tutti quelli eventualmente rimasti in F1 */ while(!feof(F1)) { fwrite(&R1,sizeof(Registro),1,FM); fread(&R1,sizeof(Registro),1,F1); } /* copia in FM di tutti quelli eventualmente rimasti in F2 */ while(!feof(F2)) { fwrite(&R2,sizeof(Registro),1,FM); fread(&R2,sizeof(Registro),1,F2); } fclose(F1); fclose(F2); fclose(FM); } }

}

Soluzione Esercizio 3 Domanda 1. Siccome gli elementi del file sono ordinati si pu applicare una tecnica simile a quella vista nell’esercizio : si scandiscono i suoi elementi e se il carattere iniziale dell’iesimo elemento e` uguale a quello precedente si incrementa una variabile di conteggio, altrimenti il valore della variabile di conteggio rappresenta il numero di volte che il carattere iniziale dell’i-esimo elemento e` presente nel file. Consideriamo prima di tutto una soluzione semplice: tra le iniziali pi`u frequenti, si individua solo la prima. Inizializzare il carattere corrente Inizializzare il contatore corrente Per ogni record A del file Se A.Parola[0] \‘e uguale al carattere corrente Allora incrementa di A.count il contatore corrente Altrimenti Settare come nuovo carattere corrente A.Parola[0] (1) Settare il contatore corrente ad A.count (2) Riportare in output il carattere e la frequenza massima

10

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

Se il contatore corrente \‘e maggiore del massimo Settare come carattere massimo il carattere corrente Settare frequenza massima al valore del contatore corrente Il carattere e il contatore corrente devono essere inizializzati con i valori relativi al primo record del file; allo scopo di fare quest’inizializzazione all’interno del ciclo di lettura del file si deve fare in modo che con il primo elemento a) si eseguano le istruzioni (1) e (2) b) non venga trattato il carattere corrente A tale scopo si potrebbero utilizzare delle opportune variabili booleane. Nella soluzione di questo esercizio si inizializzano opportunamente le variabili in gioco. Per eseguire le istruzioni (1) e (2) si deve rendere falsa la condizione A.Parola[0] e` uguale al carattere corrente: a tale scopo si inizializza il carattere corrente al valore ipotizzando quindi che non ci siano parole che iniziano con uno spazio bianco. Per non trattare il carattere corrente si fa in modo che la condizione contatore corrente maggiore del massimo all’inizio sia falsa, inizializzando sia la frequenza massima che il contatore corrente a zero. Infine, per convincersi della necessit`a di trattare il carattere corrente anche alla fine del ciclo di lettura, basta considerare come esempio il caso in cui tutte le paroli inizino con la stessa iniziale oppure il caso di file contenente un unico record. void MaggioreFrequenza(void){ FILE *FI; Registro A; char ChMax; char Ch=’ ’; int Max=0; int conta=0; if ((FI=fopen("Conta.dat","rb"))!=NULL){ while ((fread(&A,sizeof(Registro),1,FI)!=0)) if(Ch==A.Parola[0]) conta=conta+A.Count; else { if (conta>Max){ ChMax=Ch; Max=conta; } Ch=A.Parola[0]; conta=A.Count; } if (conta>Max){

4.2. SOLUZIONI

11

ChMax=Ch; Max=conta; } fclose(FI); printf("\n Massimo %c %5d",ChMax,Max); } } Nella precedente soluzione tra le iniziali pi`u frequenti, si individua solo la prima: infatti quando si tratta il carattere corrente si pu solo decidere se la sua frequenza e` quella massima sino a quel punto e per individuare quella massima in assoluto si devono necessariamente considerare tutti i record. Per poter individuare tutte le iniziali di frequenza massima, si deve essere in grado, quando si tratta un carattere, di decidere se la sua frequenza e` quella massima e questo e` possibile solo se si conosce qual e` tale massima frequenza, ovvero si deve calcolare prima il valore della frequenza massima. Il calcolo della frequenza massima avviene sostanzialmente come nella precedente soluzione (non e` necessario considerare il carattere di massima frequenza). Nota la massima frequenza, l’algoritmo e` il seguente: Inizializzare il carattere corrente Inizializzare il contatore corrente Per ogni record A del file Se A.Parola[0] \‘e uguale al carattere corrente Allora incrementa di A.count il contatore corrente Altrimenti Settare il contatore corrente ad A.count (2) Se il contatore corrente \‘e pari alla frequenza massima Riportare in output il carattere corrente e la frequenza massima Un modo per poter individuare tutte le iniziali di frequenza massima senza dover leggere due volte il file e` quello di memorizzare ciascuna iniziale incontrata durante la lettura di tutti i record con la relativa frequenza e successivamente decidere quelle di frequenza massima. In una soluzione di questo tipo le iniziali sono memorizzate man mano che si incontrano e se si incontra un’iniziale gi`a memorizzata si incrementa solo la sua frequenza: non e` necessario quindi che i record siano ordinati. Per memorizzare ciascuna iniziale potrei usare una struttura dinamica (ad esempio una lista) in quanto non si conosce a priori quali siano le iniziali delle parole dei record; lasciando al lettore questo tipo di soluzione, in questa sede si usa invece una tecnica simile a

CAPITOLO 4. FONDAMENTI DI INFORMATICA - ESERCIZI

12

quella vista nell’esercizio ... che fa uso di un vettore statico: int Conteggio[’z’-’a’ + 1]; void MaggioreFrequenzaDUE(void){ FILE *FI; int NL=’z’-’a’ + 1; int Conteggio[’z’-’a’ + 1]; char Ch; Registro A; int FMax; // massima frequenza for (Ch=0;Ch