Discussione esercizio 35:
#include <stdlib.h>
#include <string.h>
typedef struct {
char cognome[40];
char nome[20];
} Persona;
int cmpP(const void *e1, const void *e2) {
Persona *p1 = (Persona *)e1, *p2 = (Persona *)e2;
return strcmp(p1->cognome, p2->cognome);
}
void sort_pers(Persona P[], int n) {
qsort(P, n, sizeof(Persona), cmpP);
}
Discussione esercizio 36:
#include <stdlib.h>
typedef struct Elem {
int val;
struct Elem *next;
} Elem, *List;
List filter(List L, int (*f)(int v)) {
Elem **p = &L;
while (*p != NULL) {
Elem *e = *p;
if (f(e->val)) {
*p = e->next;
free(e);
} else
p = &((*p)->next);
}
return L;
}
File ad accesso sequenziale (file di testo) e file ad accesso casuale (file binari).
Rappresentazione, da un punto di vista logico, di un file: il cursore e l'effetto delle operazioni di lettura/scrittura su di esso.
Il riferimento ad un file aperto: la struct
FILE
. Le principali funzioni, della
libreria standard (
stdio.h
), per la manipolazione dei files.
-
FILE *fopen(const char *fname, const char *mode)
: apre un file di nome fname
in modalità mode
e ritorna il riferimento ad esso. Modalità di apertura (per file di testo):
"r" lettura file esistente (se non esiste ritorna NULL)
"w" scrittura (se il file esiste lo tronca, altrimenti lo crea)
"a" scrittura in append: scrive a partire dalla fine del file (se non esiste lo crea)
"r+" lettura e scrittura file esistente (se non esiste ritorna NULL)
"w+" lettura e scrittura (se il file esiste lo tronca, altrimenti lo crea)
"a+" lettura e scrittura in append: scrive (e legge) a partire dalla fine del file (se non esiste lo crea)
Per i file binari bisogna aggiungere il carattere 'b'
, ad es. "r+b"
.
-
int fclose(FILE *f)
: chiude il file f
e ritorna 0
,
se si verifica un errore ritorna EOF
.
-
void rewind(FILE *f)
: riporta il cursore di f
all'inizio del file.
-
int fgetc(FILE *f)
: ritorna il prossimo carattere (byte) del file f
e avanza il cursore. Se il cursore è
alla fine del file o si verifica un errore ritorna EOF
.
-
int fputc(int c, FILE *f)
: scrive il carattere c
nella posizione del cursore
del file f
, avanza il cursore e ritorna il carattere scritto. Se si verifica un errore, ritorna EOF
.
-
int fscanf(FILE *f, const char *format, ...)
: del tutto simile alla scanf
solo che
legge dal file f
invece che dallo standard input (scanf(...)
è equivalente a fscanf(stdin,...)
).
Ritorna il numero di assegnamenti effettuati.
-
int fprintf(FILE *f, const char *format, ...)
: del tutto simile alla printf
solo che
scrive nel file f
anziché nello standard output (printf(...)
è equivalente a fprintf(stdout,...)
).
Ritorna (in quasi tutte le implementazioni) il numero di caretteri scritti, in caso di errore ritorna un valore negativo.
char *fgets(char *s, int n, FILE *f)
: legge una linea dal file f
e la
copia in s
. Più precisamente, legge e copia i caratteri nel blocco puntato da s
finché
incontra o un newline o la fine del file o ha letto n
- 1
caratteri. La stringa s
è
sempre terminata da '\0'
. Se incontra un newline (entro i primi n
- 1
caratteri) lo aggiunge
a s
. Se incontra la fine del file senza aver letto alcun carattere ritorna NULL
(e s
rimane invariata),
altrimenti ritorna s
.
Le funzioni
fscanf
,
fprintf
e
fgets
sono usate prevalentemente per file di testo
(anche se nulla vieta di usarle per file binari). Le funzioni più adatte a leggere e scrivere file binari sono trattate nella prossima lezione.
Ecco alcuni esempi.
-
Programma che prende come argomento del main il nome di un file (di testo) e permette all'utente di stampare a video
le linee del file pagina per pagina (uso di
fopen()
,
fgets()
e fclose()
):
#include <stdio.h>
#include <string.h>
#include "util.h"
#define LINESPERPAGE 20
#define MAXLENLINE 100
int printnextpage(FILE *f) {
char line[MAXLENLINE + 1];
int count = 0;
while (count < LINESPERPAGE && fgets(line, MAXLENLINE + 1, f) != NULL) {
printf("%s", line);
if (line[strlen(line) - 1] != '\n')
printf("\n");
count++;
}
return count;
}
int main(int argc, char *argv[]) {
if (argc != 2) return 0;
FILE *f = fopen(argv[1], "r");
if (f == NULL) {
printf("Impossibile aprire il file \"%s\"\n", argv[1]);
return 0;
}
int numlines = 0;
while (1) {
int n = printnextpage(f);
numlines += n;
if (n == LINESPERPAGE) {
printf("LINEE %d - ENTER per continuare, q per uscire\n", numlines);
char in[2];
if (input_line(in, 1) == 1 && in[0] == 'q') break;
} else {
printf("TOTALE LINEE %d\n", numlines);
break;
}
}
fclose(f);
return 0;
}
-
Implementazione del modulo di memorizzazione (
archivio_mem.c
) del programma
che gestisce l'archivio dipendenti tramite file di testo. Lasciata come esercizio.
#include "archivio_mem.h"
#include <stdlib.h>
#include <stdio.h>
#define NOMEARCH "ArchivioDipendenti.txt"
static Dipendente *archivio = NULL;
static int numDipendenti = 0;
static int fgetline(FILE *f, char line[], int maxlen) {
int c, n = 0;
do {
c = fgetc(f);
if (c != EOF && c != '\n' && n < maxlen)
line[n++] = c;
} while (c != EOF && c != '\n');
line[n] = '\0';
return n;
}
static int fgetint(FILE *f) {
int n;
fscanf(f, "%d", &n);
while (fgetc(f) != '\n');
return n;
}
void open_archivio() {
FILE *f = fopen(NOMEARCH, "r+");
if (f == NULL)
f = fopen(NOMEARCH, "w+");
if (f == NULL) {
printf("ERRORE in apertura archivio\n");
exit(1);
}
Dipendente d;
while (fgetline(f, d.cognome, MAXL_COG) > 0) {
fgetline(f, d.nome, MAXL_NOM);
Data *dn = &(d.nascita);
fscanf(f, "%d/%d/%d", &(dn->g), &(dn->m), &(dn->a));
d.ruolo = fgetint(f);
switch (d.ruolo) {
case IMP: d.info.liv = fgetint(f);
break;
case DIR: fgetline(f, d.info.sez, MAXL_SEZ);
break;
case DIRSUP: fgetline(f, d.info.dip, MAXL_DIP);
break;
}
add_dipendente(d);
}
fclose(f);
}
void add_dipendente(Dipendente d) {
archivio = realloc(archivio, (numDipendenti + 1)*sizeof(Dipendente));
archivio[numDipendenti++] = d;
}
int num_dipendenti() {
return numDipendenti;
}
Dipendente get_dipendente(int i) {
return archivio[i];
}
void close_archivio() {
FILE *f = fopen(NOMEARCH, "w");
if (f == NULL) {
printf("ERRORE in apertura archivio\n");
exit(1);
}
for (int i = 0; i < numDipendenti; i++) {
Dipendente d = archivio[i];
fprintf(f, "%s\n", d.cognome);
fprintf(f, "%s\n", d.nome);
fprintf(f, "%d/%d/%d\n", d.nascita.g, d.nascita.m, d.nascita.a);
fprintf(f, "%d\n", d.ruolo);
switch (d.ruolo) {
case IMP: fprintf(f, "%d\n", d.info.liv);
break;
case DIR: fprintf(f, "%s\n", d.info.sez);
break;
case DIRSUP: fprintf(f, "%s\n", d.info.dip);
break;
}
}
fclose(f);
}
Esercizio 37
Scrivere un programma che prende come argomenti del main i nomi di due file di testo,
legge tutte le parole
contenute nel primo file memorizzandole in un array di stringhe, ordina l'array tramite
qsort()
e scrive nel secondo file le parole distinte (senza ripetizioni) e ordinate,
una per linea.
Suggerimenti: il primo file va aperto in lettura ("r"
), il secondo in
scrittura ("w"
), per leggere le parole modificare la funzione
next_word()
del programma che conta le parole distinte.
Esercizio 38
Scrivere un programma che prende come argomenti del main i nomi di due file di testo e
appende il contenuto del secondo file al primo file. Ad esempio, se il primo file contiene
Testo primo file
e il secondo file contiene
Testo secondo file
allora il programma modifica il
primo file così
Testo primo fileTesto secondo file
.