001: #include<stdio.h>
002: #include<stdlib.h>
003: #include<float.h>
004: 
005: // definizione struttura per #2
006: // di fatto usero' un array di queste struct in cui l'indice dell'elemento corrisponde all'ID sensore
007: struct sensore
008: {
009:   unsigned short nmis;     // numero misure
010:   double         avg;      // valor medio
011:   double         min, max; // minimo e massimo 
012:   unsigned short ncresc;   // lunghezza massima sequenza crescente
013:   unsigned int   newest;   // data misura piu' recente (la memorizzo come singolo unsigned int senza spezzettare in giorno, mese e anno)
014: };
015: 
016: int confrontadate(unsigned int, unsigned int);  // funzione che uso per confrontare le date delle misure, per leggibilita'
017: unsigned short giorno(unsigned int);            // estrae il giorno dalla data memorizzata come unsigned int
018: unsigned short mese(unsigned int);              // estrae il mese dalla data memorizzata come unsigned int
019: unsigned short anno(unsigned int);              // estrae l'anno dalla data memorizzata come unsigned int
020: 
021: int main(int argc, char **argv){
022: 
023:   // per #1 capisco se nome file era passato su linea comando altrimenti chiedo all'utente
024:   FILE *fp;
025:   if(argc < 2)
026:   {
027:     char nome_file[1000];
028:     printf("Inserisci il nome del file da analizzare: ");
029:     scanf("%s", nome_file);
030:     fp = fopen(nome_file, "r");
031:   }
032:   else
033:   {
034:     fp = fopen(argv[1], "r");
035:   }
036:   // Nota: in base al testo potevo comunque limitarmi a una sola delle due alternative
037: 
038:   if(!fp)
039:   {
040:     perror("");
041:     exit(EXIT_FAILURE);
042:   }
043: 
044:   // #3 definisco array vuoto
045:   struct sensore *dati = NULL;
046:   int nsensors         = 0;
047: 
048:   // temporanei per lettura da file
049:   unsigned int data;   // di fatto e' vedibile come numero e l'intervallo possibile e' coerente con un unsigned int ~ 20 milioni
050:   unsigned int sensor; // id sensore
051:   double misura;
052: 
053:   unsigned int ndati = 0;
054:   while(fscanf(fp, "%u;%u;%lf", &data, &sensor, &misura) == 3)
055:   {
056:     ++ndati;
057:     if(sensor >= nsensors) // nell'array l'elemento di indice 'sensor' ancora non e' presente, devo allargare l'array
058:     {
059:       dati = realloc(dati, sizeof(struct sensore)*(sensor+1));  
060:       // devo inizializzare quanto aggiunto all'array
061:       // ma non e' detto che io abbia aggiunto un solo valore
062:       for(int i = nsensors; i < (sensor + 1); ++i)
063:       {
064:   dati[i].nmis    = 0;
065:   dati[i].avg     = 0;
066:   dati[i].min     = 1001;   // oltre intervallo specifiche
067:   dati[i].max     = -1001;  // sotto intervallo specifiche
068:   dati[i].newest  = 0;      // valore insensato per data ma comunque sufficientemente basso
069:       }
070:       nsensors = sensor + 1;
071:     }
072: 
073:     // aggiorno elemento corrispondente al sensore
074:     dati[sensor].nmis++;
075:     dati[sensor].avg += misura; // in fase di lettura accumulo qui i valori, la media vera e propria la calcolerò finita la lettura
076: 
077:     // aggiorno eventualmente massimo e minimo valore letto
078:     if(misura < dati[sensor].min)
079:       dati[sensor].min = misura;
080: 
081:     if(misura > dati[sensor].max)
082:       dati[sensor].max = misura;
083: 
084: 
085:     // stessa cosa per la data, ma qui sfrutto opportuna funzione per confrontare
086:     if(confrontadate(dati[sensor].newest, data) < 0)
087:       dati[sensor].newest = data;
088:   }
089:   fclose(fp);
090: 
091:   // a valle della lettura calcolo media
092:   for(int i = 0; i < nsensors; ++i)
093:   {
094:     if(dati[i].nmis)
095:       dati[i].avg = dati[i].avg/dati[i].nmis;   
096:   }
097: 
098:   // #4 stampo dati inerenti struttura
099:   printf("Ho letto dal file '%s' %d misure distribuite su %d sensori\n", argv[1], ndati, nsensors);
100:   for(int i = 0; i < nsensors; ++i)
101:   {
102:     if(dati[i].nmis)
103:       printf("#%02d: %3u misur%c, media %8g, min. %8g, max. %8g, ultimo %02u/%02u/%4u\n", 
104:     i,                                // id sensore
105:     dati[i].nmis,                     // numero misure di quel sensore
106:     (dati[i].nmis>1?'e':'a'),         // lo uso per scrivere misure o misura
107:     dati[i].avg,                      // media
108:     dati[i].min,                      // minimo
109:     dati[i].max,                      // massimo
110:     giorno(dati[i].newest),           // giorno ultima misura
111:     mese(dati[i].newest),             // mese ultima misura
112:     anno(dati[i].newest)              // anno ultima misura
113:     );
114:     else
115:       printf("#%02d: NA\n", i);
116:   }
117: 
118:   // #5 stampo occupazione memoria. Devo considerare: dimensione area allocata, dimensione puntatore e dimensione variabile d'appoggio per stimare numero elementi)
119:   printf("Occupazione memoria: %ld byte\n", sizeof(dati[0])*nsensors + sizeof(dati) + sizeof(nsensors));
120: 
121:   return 0;
122: }
123: 
124: 
125: // nelle seguenti funzioni sfrutto divisioni intere e moduli per spezzettare la data in giorni, mese, anno
126: // ad esempio supponiamo che la data sia il 9 febbraio 2023. Nel file avro' avuto 09022023. Se lo leggo come numero otterro' 9022023
127: // il giorno lo ricavo semplicamente dividendo (divisione intera) per 1000000 -> 9022023/1000000 = 9
128: // l'anno e' il resto della divisione intera per 10000 -> 9022023%10000 = 2023
129: // il mese e' un po' piu complesso, prima prendo il resto della divisione per 1000000 -> 9022023%1000000 = 022023 (ovvero 22023)
130: // e quindi divido (sempre divisione intera) per 10000 -> 22023/10000 = 2
131: 
132: unsigned short giorno(unsigned int data)
133: {
134:   return data/1000000;  // le prime due cifre sono il giorno, posso usare la divisione intera per 1000000 per estrarle
135: }
136: 
137: unsigned short mese(unsigned int data)
138: {
139:   return (data%1000000)/10000; // prendo le 6 cifre meno significative con il resto della divisione intera per 1000000 e poi elimino le 4 meno significative dividendo per 10000
140: }
141: 
142: unsigned short anno(unsigned int data)
143: {
144:   return data%10000; // le 4 cifre meno significative sono l'anno, le estraggo con il resto della divisione per 10000
145: }
146: 
147: // prendo in ingresso le due date come unsigned int
148: // sfrutto le funzioni precedentemente definite per estrarre giorno, mese e anno
149: // e poi li confronto
150: // la funzione restituisce 0 se le due date sono identiche
151: // < 0 se a precede b
152: // > 0 nel caso opposto
153: int confrontadate(unsigned int a, unsigned int b)
154: {
155:   unsigned short aa = anno(a);
156:   unsigned short ab = anno(b);
157: 
158:   unsigned short ma = mese(a);
159:   unsigned short mb = mese(b);
160: 
161:   unsigned short ga = giorno(a);
162:   unsigned short gb = giorno(b);
163: 
164:   if(aa != ab)
165:     return aa-ab;
166: 
167:   if(ma != mb)
168:     return ma-mb;
169: 
170:   return ga-gb;
171: }
172: