001: #include<stdio.h>
002: #include<stdlib.h>
003: #include<string.h>
004: #include<ctype.h>
005:
006:
007: // prototipi funzioni da definire
008: char *readstring(FILE*);
009: double convertnum(char*);
010:
011: int main(int argc, char **argv){
012:
013: FILE *fp = fopen(argv[1], "r"); // per comodita' passo il nome del file sulla riga di comando, il testo non diceva nulla in merito e quindi ogni scelta ragionevole e' valida
014: if(!fp)
015: {
016: perror("");
017: exit(EXIT_FAILURE);
018: }
019:
020: char *s; // la uso per memorizzare stringa restituita da readstring()
021:
022: int inizio = 1; // per capire se e' il primo numero o sono dopo un =
023: double acc, operand; // dello stesso tipo restituito da convertnum()
024: while( (s = readstring(fp)) != NULL)
025: {
026: if( !strcmp(s, "+") )
027: {
028: acc = acc + operand;
029: }
030: else if( !strcmp(s, "-") )
031: {
032: acc = acc - operand;
033: }
034: else if( !strcmp(s, "*") )
035: {
036: acc = acc * operand;
037: }
038: else if( !strcmp(s, "/") )
039: {
040: acc = acc / operand; // il testo non chiedeva di effettuare controlli sugli operandi, quindi non controllo se divisione per 0
041: }
042: else if( !strcmp(s, "=") )
043: {
044: printf("%lf\n", acc);
045: inizio = 1; // dopo un "=" e' come se ripartissi da 0
046: }
047: else // per esclusione
048: {
049: double num = convertnum(s);
050: // printf("Ho letto %s -> %lf\n", s, num); // DEBUG
051: if(inizio) // se primo numero letto o comunque primo numero dopo un "=" devo memorizzare in acc, altrimenti in operand
052: {
053: inizio = 0;
054: acc = num;
055: }
056: else
057: {
058: operand = num;
059: }
060: }
061:
062: free(s); // non mi serve piu' e disalloco
063: }
064:
065: fclose(fp);
066:
067:
068:
069:
070: return 0;
071: }
072:
073: char *readstring(FILE *f)
074: {
075: char tmp[10000];
076: int letti = fscanf(f, " %s", tmp); // come visto a lezione, fscanf() restituisce il numero di specificatori di formato che e' riuscita a risolvere. Quindi se legge una stringa deve restituire 1
077: if( letti != 1 )
078: return NULL;
079:
080: // alloco area dinamica su cui ricopiare la stringa e tengo conto del carattere nullo di terminazione
081: char *x = malloc(strlen(tmp) + 1);
082: // vi copio quanto letto
083: strcpy(x, tmp);
084: // lo restituisco
085: return x;
086: }
087:
088:
089: double convertnum(char *s)
090: {
091: // PRIMA PARTE valutiamo se la stringa contiene realmente un numero
092:
093: // test 1: deve contenere solo cifre oppure ., +, -, e o E
094: // scorro carattere per carattere e controllo
095: for(int i = 0; i < strlen(s); ++i)
096: {
097: if( !isdigit(s[i]) && s[i] != '+' && s[i] != '-' && s[i] != '.' && s[i] != 'e' && s[i] != 'E' )
098: {
099: printf("ERRORE: la stringa %s contiene il carattere non valido [%c] \n", s, s[i]);
100: exit(EXIT_FAILURE);
101: }
102: // per semplicita' di operazioni nel seguito, trasformo eventuale e minuscola in E maiuscola
103: if( s[i] == 'e' )
104: s[i] = 'E';
105: }
106:
107: // test 2: il primo carattere deve essere solo una cifra oppure ., +, -
108: if( !isdigit(s[0]) && s[0] != '.' && s[0] != '+' && s[0] != '-' )
109: {
110: printf("ERRORE: la stringa %s ha il primo carattere non valido\n", s);
111: exit(EXIT_FAILURE);
112: }
113:
114: // nota, da qui in avanti uso le funzioni di stringa strchr() e strrchr() che mi cercano un carattere all'interno di una stringa e restituiscono il suo indirizzo se lo trovano o NULL se non c'e'.
115: // dato che una cerca da destra e una da sinistra posso usarle per capire se ci sono almeno due caratteri di quel tipo
116: // confrontando gli indirizzi restituiti si puo' inoltre capire se un carattere ricercato e' prima o dopo di un altro
117:
118: // test 3: solo un "." e prima di eventuali E o e
119: char *firstdec = strchr(s, '.'); // cerco posizione eventuale punto partendo da sinistra
120: char *lastdec = strrchr(s, '.'); // cerco posizione eventuale punto partendo da destra
121: // se c'e un punto solo, che io lo cerchi a partire da sinistra o a partire da destra devo avere lo stesso risultato
122: if( firstdec != lastdec )
123: {
124: printf("ERRORE: la stringa %s ha troppi punti decimali\n", s);
125: exit(EXIT_FAILURE);
126: }
127: char *firstE = strchr(s, 'E'); // cerco posizione eventuale E partendo da sinistra
128: // se c'e' un . e una E allora il . deve precedere la E
129: if(firstE && firstdec && firstE < firstdec) // firstE e firstdec sono NULL se non c'e' quel carattere, quindi li confronto se e solo se tutti e due non sono NULL
130: {
131: printf("ERRORE: la stringa %s ha il punto decimale dopo l'indicazione dell'esponente\n", s);
132: exit(EXIT_FAILURE);
133: }
134:
135: // test 4: la E deve apparire una sola volta ed essere seguita da almeno una cifra eventualmente preceduta da + o -
136: char *lastE = strrchr(s, 'E'); // cerco posizione eventuale E partendo da destra (da sinistra l'avevo gia' calcolata)
137: if( firstE != lastE )
138: {
139: printf("ERRORE: la stringa %s ha troppe indicazioni dell'esponente\n", s);
140: exit(EXIT_FAILURE);
141: }
142: // se la E esiste guardo il carattere immediatamente successivo saltando eventuale segno
143: if( firstE )
144: {
145: ++firstE; // incremento il puntatore che ora puntera' al carattere che segue la E
146: if( *firstE == '+' || *firstE == '-' ) // se il carattere successivo e' un + o un - incremento ancora
147: ++firstE;
148: if( !isdigit(*firstE) )
149: {
150: printf("ERRORE: la stringa %s non contiene un numero dopo l'indicazione dell'esponente\n", s);
151: exit(EXIT_FAILURE);
152: }
153: }
154:
155: // SECONDA PARTE converto stringa in numero (se arrivo qui, sicuramente il numero e' corretto) e lo restituisco
156:
157: return atof(s);
158:
159:
160: }
161: