Stazione di monitoraggio 4 - Soluzione
La prima cosa da fare è realizzare la struttura che ospiterà i dati delle singole righe. Un esempio è il seguente:
struct misura {
int aa, MM, gg; // data
int hh, mm, ss, ms; // orario
char id[MAX_ID_LUNG + 1]; // identificativo del dispositivo
float temp; // temperatura
int umid; // umidità
float vel; // velocità del vento
};
NOTA: il nome dei campi è abbreviato per velocizzare la scrittura del codice. Ciononostante, gli identificatori utilizzati sono sufficientemente esplicativi da permetterne una immediata comprensione.
La parte più interessante di questo problema è la funzione che si occupa della lettura del file. Un esempio di funzione di lettura è la seguente:
struct misura *leggi_file(FILE *f, int *n)
{
struct misura *m, *elenco, *tmp_ptr;
char buf[1000];
int nconv; // numero di elementi convertiti
int dim;
/* inizializza a zero il numero di strutture caricate */
*n = 0;
/* dimensionamento e allocazione iniziale del vettore */
dim = 4;
elenco = malloc(dim * sizeof(*elenco));
/* ad ogni iterazione, legge una riga fino alla fine del file */
while (fgets(buf, sizeof(buf), f)) {
/* punta all'elemento corrente da memorizzare */
m = elenco + (*n);
nconv = sscanf(buf, "%d-%d-%d %d:%d:%d.%d %s %f %d%% %f",
&(m->aa), &(m->MM), &(m->gg),
&(m->hh), &(m->mm), &(m->ss), &(m->ms),
(m->id), &(m->temp), &(m->umid), &(m->vel));
/* passa alla riga successiva se non ci sono esattamente 11
* elementi da convertire */
if (nconv != 11) continue;
/* se si arriva qui, la riga è stata letta e memorizzata
* correttamente; incremento il numero di righe lette */
(*n)++;
/* verifica se il vettore è riempito */
if ((*n) >= dim) {
/* raddoppia il valore della variabile */
dim *= 2;
/* ri-alloca lo spazio per il vettore */
tmp_ptr = realloc(elenco, dim * sizeof(*elenco));
/* controlla che la ri-allocazione abbia avuto successo */
if (tmp_ptr == NULL) return NULL;
/* se è tutto a posto, assegna il puntatore */
elenco = tmp_ptr;
}
}
/* riduce la dimensione del vettore fissandola pari a *n */
elenco = realloc(elenco, (*n) * sizeof(*elenco));
/* restituisce il vettore con i dati letti */
return elenco;
}
Non c'è da spaventarsi della lunghezza, in quanto buona parte del codice è costituito da commenti. I commenti dovrebbero essere sufficienti per capire la logica del programma.
La funzione restituisce un puntatore al vettore allocato all'interno della funzione.
I parametri sono due:
- il file
f
, già aperto in lettura prima di chiamare laleggi_file
; - il puntatore all'intero
n
che serve a restituire il numero di righe lette tramite il passaggio di parametri per indirizzo.
Il puntatore elenco
, all'interno della funzione leggi_file
, viene assegnato tramite la malloc
, che alloca una quantità di memoria pari a dim
elementi di tipo struct misura
.
Il valore di dim
è inizialmente pari a 4
.
Il ciclo while
legge dal file una riga ad ogni iterazione tramite la fgets
.
Per quanto riguarda la condizione del ciclo
while
, ricorda che lafgets
restituisce l'indirizzo del puntatore usato per contenere la stringa letta da file (quindi, in ogni caso, un puntatore diverso daNULL
). Se non ci sono altre righe da leggere, lafgets
restituisceNULL
. Dal momento che la costanteNULL
equivale al numero0
, la condizione del ciclowhile
diventa0
(cioè falsa) quando la lettura del file è terminata. E di conseguenza il ciclowhile
termina a sua volta.
Con la sscanf
vengono estratti i valori dalla riga letta e assegnati ai vari campi della struttura corrente nel vettore.
La parte più interessante è il ridimensionamento del vettore in caso siano stati lette un numero di righe pari a dim
.
In tal caso il valore di dim
viene raddoppiato con l'istruzione dim *= 2
(vedi S11.3.2 per la descrizione delle forme abbreviate di assegnamento).
Dopodiché la realloc
cambia la dimensione del vettore utilizzando la dimensione raddoppiata, assegnando l'indirizzo ottenuto a tmp_ptr
.
Se quest'ultimo vale NULL
allora l'allocazione non ha avuto successo, e la funzione termina restituendo NULL
, altrimenti si assegna tmp_ptr
ad elenco
e si passa a leggere la riga successiva.
Stampa invertita degli identificativi
La funzione che stampa gli identificativi in senso inverso è molto semplice e, ad esempio, può essere implementata come segue:
void stampa_identificativi(struct misura *elenco, int n)
{
int i;
for (i = n - 1; i >= 0; i--)
printf("%s\n", (elenco + i)->id);
}
Essa riceve come argomenti il vettore delle strutture caricate da file e il numero di elementi nel vettore.
Effettua un ciclo for
con contatore che parte dall'ultimo elemento del vettore (indice n - 1
) e decrementa il contatore fino ad indirizzare l'elemento di indice 0
del vettore.