Discussione esercizio 37:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *fnext_word(FILE *f) {
char *w = NULL;
int c, n = 0;
do {
c = fgetc(f);
if (isalpha(c)) {
w = realloc(w, n + 2);
w[n++] = c;
}
} while (c != EOF && (n == 0 || isalpha(c))) ;
if (w != NULL) w[n] = '\0';
return w;
}
int wcmp(const void *e1, const void *e2) {
char *w1 = *(char **)e1, *w2 = *(char **)e2;
return strcmp(w1, w2);
}
int main(int argc, char *argv[]) {
if (argc != 3) return 0;
FILE *fin = fopen(argv[1], "r");
FILE *fout = fopen(argv[2], "w");
if (fin == NULL || fout == NULL) {
printf("Impossibile aprire i files\n");
return 0;
}
char **wA = NULL, *w;
int i, nw = 0;
while ((w = fnext_word(fin)) != NULL) {
wA = realloc(wA, (nw + 1)*sizeof(char *));
wA[nw++] = w;
}
qsort(wA, nw, sizeof(char *), wcmp);
int wcount = 0;
for (i = 0 ; i < nw ; i++) {
wcount++;
if (i == nw - 1 || strcmp(wA[i], wA[i + 1]) != 0) {
fprintf(fout, "%5d %s\n", wcount, wA[i]);
wcount = 0;
}
free(wA[i]);
}
free(wA);
fclose(fin);
fclose(fout);
printf("Numero parole: %d\n", nw);
return 0;
}
Discussione esercizio 38:
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 3) return 0;
FILE *f1 = fopen(argv[1], "a");
FILE *f2 = fopen(argv[2], "r");
if (f1 == NULL || f2 == NULL) {
printf("Impossibile aprire uno dei files!\n");
return 0;
}
int c;
while ((c = fgetc(f2)) != EOF)
fputc(c, f1);
fclose(f1);
fclose(f2);
return 0;
}
Funzioni per il posizionamento del cursore dei files aperti.
-
int fseek(FILE *f, long offset, int wherefrom)
: posiziona il cursore del file f
secondo wherefrom
e offset
. Il parametro wherefrom
specifica da dove deve essere
calcolato l'offset
e può assumere solamente tre valori costanti con i seguenti significati:
SEEK_SET dall'inizio del file
SEEK_CUR dalla posizione corrente
SEEK_END dalla fine del file
Ad esempio, se f
è un file binario:
fseek(f, 100, SEEK_SET) posiziona il cursore 100 bytes dall'inizio del file
fseek(f, 100, SEEK_CUR) posiziona il cursore 100 bytes dopo la posizione corrente
fseek(f, 100, SEEK_END) posiziona il cursore 100 bytes dopo la fine del file
in tutti i casi se la posizione è oltre la fine del file, il file è
esteso con contenuto non specificato.
fseek(f, -100, SEEK_CUR) posiziona il cursore 100 bytes prima delle posizione corrente
Per i file di testo sono permesse solamente le seguenti forme (eccetto per quelle piattaforme, come Unix/Linux, in cui non
c'è differenza tra file di testo e file binari):
fseek(f, 0, SEEK_SET) equivale a rewind(f)
fseek(f, 0, SEEK_END) posiziona il cursore alla fine del file
fseek(f, ftell_pos, SEEK_SET) dove ftell_pos è una posizione ritornata da ftell(f),
e quindi riporta il cursore in quella posizione
La funzione fseek
ritorna zero, ma se si verifica un errore ritorna un valore diverso da zero.
-
long ftell(FILE *f)
: ritorna la posizione del cursore del file f
, se c'è
un errore ritorna -1
. Per i file binari il valore ritornato è il numero di bytes dall'inizio
del file, per i file di testo questo non è garantito. Comunque il valore ritornato può essere usato
come parametro offset della funzione fseek
.
Uso delle funzioni
ftell()
e
fseek()
in una funzione che cerca
la prima occorrenza di una stringa in un file di testo:
#include <stdio.h>
#include <string.h>
long filefind(FILE *f, char *str) {
long n = strlen(str) + 1, pos = ftell(f);
char buffer[n];
while (fgets(buffer, n, f) != NULL) {
if (strcmp(buffer, str) == 0)
return pos;
else {
fseek(f, pos, SEEK_SET);
fgetc(f);
pos = ftell(f);
}
}
return -1;
}
int main(int argc, char *argv[]) {
if (argc != 3) return 0;
FILE *f = fopen(argv[1], "r");
if (f == NULL) {
printf("Impossibile aprire file \"%s\"\n", argv[1]);
return 0;
}
long pos = filefind(f, argv[2]);
if (pos >= 0)
printf("Trovata in posizione %ld\n", pos);
else
printf("Non trovata\n");
fclose(f);
return 0;
}
Funzioni per leggere e scrivere file binari:
-
size_t fread(void *ptr, size_t block_size, size_t count, FILE *f)
: legge dal file binario f
count
blocchi consecutivi ognuno di dimensione block_size
e li copia nel
blocco di memoria puntato da ptr
(in totale sono letti count*block_size
bytes).
Ritorna il numero di blocchi letti, se si verifica un errore
ritorna 0
.
-
size_t fwrite(void *ptr, size_t block_size, size_t count, FILE *f)
: scrive nel file binario f
count
blocchi consecutivi di dimensione ognuno block_size
letti dalla memoria a
partire dall'indirizzo ptr
(in totale sono scritti count*block_size
bytes).
Ritorna il numero di blocchi scritti. Se si verifica un errore
il numero ritornato è diverso da count
.
-
int fflush(FILE *f)
: svuota il buffer del file f
e ritorna 0
, se si verificano errori
ritorna EOF
. A volte è utile svuotare il buffer per garantire che ciò che è stato scritto
nel file sia effettivamente scritto su disco. Inoltre, tra una operazione di scrittura e una operazione di
lettura deve esserci una chiamata che svuota il buffer (fflush
, fseek
o rewind
).
Lo stesso tra una operazione di lettura e una di scrittura.
Implementazione del modulo di memorizzazione del programma che gestisce
un archivio di dipendenti tramite file binari (uso di
fread()
,
fwrite()
e
fseek()
).
#include "archivio_mem.h"
#include <stdlib.h>
#include <stdio.h>
#define NOMEARCH "ArchivioDipendenti"
static FILE *archivio = NULL;
static int numDipendenti = 0;
void open_archivio() {
archivio = fopen(NOMEARCH, "r+b");
if (archivio == NULL)
archivio = fopen(NOMEARCH, "w+b");
if (archivio == NULL) {
printf("ERRORE in apertura archivio\n");
exit(1);
}
Dipendente d;
while (fread(&d, sizeof(Dipendente), 1, archivio) == 1)
numDipendenti++;
}
void add_dipendente(Dipendente d) {
fseek(archivio, numDipendenti*sizeof(Dipendente), SEEK_SET);
fwrite(&d, sizeof(Dipendente), 1, archivio);
fflush(archivio);
numDipendenti++;
}
int num_dipendenti() {
return numDipendenti;
}
Dipendente get_dipendente(int i) {
Dipendente d;
fseek(archivio, i*sizeof(Dipendente), SEEK_SET);
fread(&d, sizeof(Dipendente), 1, archivio);
return d;
}
void close_archivio() {
fclose(archivio);
archivio = NULL;
numDipendenti = 0;
}
Esercizio 39
Scrivere un funzione
int filenumocc(FILE * f, char *s)
che ritorna il numero di occorrenze
della stringa
s
nel file di testo
f
.
Esercizio 40
Scrivere un programma che prende come argomento il nome di un file e se non esiste
lo crea e vi scrive il primo milione di numeri primi (in ordine crescente). Poi permette
all'utente di inserire interi
k
e per ogni
k
stampa
il
k
-esimo numero primo, leggendolo dal file. Il file è binario
e i numeri primi sono memorizzati come
int
. Ad esempio, se l'utente
inserisce
k = 1
il programma stampa
2
, se
k = 1000
il programma stampa
7919
e se
k = 1000000
il programma
stampa
15485863
(il milionesimo numero primo).