[C] Problema lettura da stringhe

di il
7 risposte

[C] Problema lettura da stringhe

Buongiorno a tutti,
sto preparando l'esame di fondamenti di informatica, di programmazione in C.
Per farla breve devo leggere un file in cui sono contenute delle righe, e su ogni riga ci sono 4 numeri separati da uno spazio.
Con un ragazzo (molto preparato) che mi dà ripetizioni abbiamo creato una funzione che legge le righe tramite la combinazione fgets + sscanf.
Ho riscontrato un problema: la sscanf legge solo il primo numero (per come mi ha aiutato a scriverla quel ragazzo).
Sfortunatamente mi sono accorto del problema quando ormai ero a casa, non l'abbiamo controllata perché la lezione era finita e non sono ancora riuscito a contattarlo... Lo rivedrò tra qualche giorno, ma il tempo stringe perché l'esame è vicino...
C'è un modo per non cambiare funzione, e quindi leggere la stringa intera tramite sscanf?

Codice: la funzione è leggi_dati

Grazie in anticipo
PS: se necessario vi posto anche il testo dell'esame
#include <stdio.h>		//Fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo delle venerabili funzioni printf e scanf.
#include <stdlib.h>		//Per eseguire un gran numero di operazioni, tra cui conversioni, generazione di numeri pseudo-casuali, allocazione di memoria, controllo del processo, variabili 							d'ambiente, segnali, ricerca ed ordinamento.
#include <string.h>		//Per manipolare le stringhe.

#define CIFRE_CODICE 4
#define MAX_TENTATIVI 5
#define PARTITE 6
#define MAX_PARTITE (6 * (5 + 1))


/* Strutture usate */

typedef struct CODICE {
	char cod[8];
	// int num_giocatore;
	char tipo;				 // 'C' se è un codice da indovinare, 'T' se è un codice scritto dal decodificatore e quindi si tratta di un tentativo
} codice;

/* Vettore di strutture (*v) con dimensione (n) */
typedef struct ELENCO_CODICI {
	codice *v;
	int n;
} elenco_codici;


/* Funzioni usate */

/* Funzione che legge i dati e li sistema bene nella struttura partita */

elenco_codici leggi_dati(char *argv[]) {
	
	/* Apertura file, dichiarazioni di variabili */
	FILE *fp;
	fp = fopen(argv[1], "r");
	char buffer[8];
	int i = 0;
	int x;
	int n = MAX_PARTITE;
	codice tmp;
	codice *v = malloc(sizeof(codice)*n);
	
	/* C'è un errore */
	while(fgets(buffer, sizeof(buffer), fp) != NULL) {
		
		x = sscanf(buffer, "%s", tmp.cod);
		if (x==1) {
			v[i] = tmp;
			i++;
		}
	}
	
	n = i;
	v = realloc(v, sizeof(codice)*n);
	fclose(fp);
	
	/* Stampa di prova, da cui rilevo l'errore */
	int k;
	for (k = 0; k < n; k++) {
			printf("%s\n", v[k].cod);
	}
	
	/* Struttura elenco codici, con dentro l'elenco e il numero di codici totali */
	elenco_codici E;
	E.v = v;
	E.n = n;
	return E;
}


/* Funzione che analizza un codice: se è da indovinare assegna 'C' al campo "tipo"
   della struct CODICE, altrimenti gli assegna 'T' (tentativo). */
   
void iscode_ortry(codice *v, int n) {
	
	int i = 0;
	int j = 1;
	/* A ogni giro del ciclo, questo for salta al prossimo codice da indovinare */
	for (i = 0; i < n; i = j + 1) {
		v[i].tipo = 'C';
		/* Finché i codici sono diversi e ci troviamo entro i 5 tentativi, allora il codice j-esimo è un tentativo T */
		while (strcmp(v[i].cod, v[j].cod) != 0 && j < (i + MAX_TENTATIVI + 1) ) {
			v[j].tipo = 'T';
			j++;
		}
		v[j].tipo = 'T';
		
	}
	
	
	
	for (i = 0; i < n; i++) {
		printf("%c\n", v[i].tipo);
	}
	
	
}

int main(int argc, char *argv[]) {
	
	elenco_codici E;
	E = leggi_dati(argv);
	iscode_ortry(E.v);
	
	return 0;
}



7 Risposte

  • Re: [C] Problema lettura da stringhe

    Intanto quel codice non è compilabile.

    Nella linea

    iscode_ortry(E.v);

    passi un solo parametro. La funzione ne prevede due.
  • Re: [C] Problema lettura da stringhe

    Sì perdonami, erano le 2 ed ero un po' fuori di testa ieri sera.
    Per ora ho trovato una scappatoia inserendo 4 interi nella struttura e lavorando con quelli... Ma rinnovo la domanda: perseverando con la stringa intera non c'è modo?
    Codice corretto
    #include <stdio.h>      //Fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo delle venerabili funzioni printf e scanf.
    #include <stdlib.h>      //Per eseguire un gran numero di operazioni, tra cui conversioni, generazione di numeri pseudo-casuali, allocazione di memoria, controllo del processo, variabili                      d'ambiente, segnali, ricerca ed ordinamento.
    #include <string.h>      //Per manipolare le stringhe.
    
    #define CIFRE_CODICE 4
    #define MAX_TENTATIVI 5
    #define PARTITE 6
    #define MAX_PARTITE (6 * (5 + 1))
    
    
    /* Strutture usate */
    
    typedef struct CODICE {
       char cod[8];
       // int num_giocatore;
       char tipo;             // 'C' se è un codice da indovinare, 'T' se è un codice scritto dal decodificatore e quindi si tratta di un tentativo
    } codice;
    
    /* Vettore di strutture (*v) con dimensione (n) */
    typedef struct ELENCO_CODICI {
       codice *v;
       int n;
    } elenco_codici;
    
    
    /* Funzioni usate */
    
    /* Funzione che legge i dati e li sistema bene nella struttura partita */
    
    elenco_codici leggi_dati(char *argv[]) {
       
       /* Apertura file, dichiarazioni di variabili */
       FILE *fp;
       fp = fopen(argv[1], "r");
       char buffer[8];
       int i = 0;
       int x;
       int n = MAX_PARTITE;
       codice tmp;
       codice *v = malloc(sizeof(codice)*n);
       
       /* C'è un errore */
       while(fgets(buffer, sizeof(buffer), fp) != NULL) {
          
          x = sscanf(buffer, "%s", tmp.cod);
          if (x==1) {
             v[i] = tmp;
             i++;
          }
       }
       
       n = i;
       v = realloc(v, sizeof(codice)*n);
       fclose(fp);
       
       /* Stampa di prova, da cui rilevo l'errore */
       int k;
       for (k = 0; k < n; k++) {
             printf("%s\n", v[k].cod);
       }
       
       /* Struttura elenco codici, con dentro l'elenco e il numero di codici totali */
       elenco_codici E;
       E.v = v;
       E.n = n;
       return E;
    }
    
    
    /* Funzione che analizza un codice: se è da indovinare assegna 'C' al campo "tipo"
       della struct CODICE, altrimenti gli assegna 'T' (tentativo). */
       
    void iscode_ortry(codice *v, int n) {
       
       int i = 0;
       int j = 1;
       /* A ogni giro del ciclo, questo for salta al prossimo codice da indovinare */
       for (i = 0; i < n; i = j + 1) {
          v[i].tipo = 'C';
          /* Finché i codici sono diversi e ci troviamo entro i 5 tentativi, allora il codice j-esimo è un tentativo T */
          while (strcmp(v[i].cod, v[j].cod) != 0 && j < (i + MAX_TENTATIVI + 1) ) {
             v[j].tipo = 'T';
             j++;
          }
          v[j].tipo = 'T';
          
       }
       
       
       
       for (i = 0; i < n; i++) {
          printf("%c\n", v[i].tipo);
       }
       
       
    }
    
    int main(int argc, char *argv[]) {
       
       elenco_codici E;
       E = leggi_dati(argv);
       iscode_ortry(E.v, E.n);
       
       return 0;
    }
    
    
    
  • Re: [C] Problema lettura da stringhe

    Per provare il codice, mostra il file di dati che usi in input
  • Re: [C] Problema lettura da stringhe

    3 5 7 8
    3 3 3 3
    3 3 3 8
    3 3 6 8
    9 3 7 8
    3 5 7 8
    4 0 7 3
    4 0 4 3
    7 3 1 3
    0 0 7 3
    7 3 4 5
    7 5 1 1
    9 1 3 8
    5 5 9 3
    9 1 7 8
    9 1 3 8
    7 3 4 8
    7 3 0 5
    7 3 4 2
    7 3 4 3
    4 1 7 2
    2 4 4 2
    6 9 7 8
    6 9 7 8
    9 9 9 9
    2 1 3 9
    2 9 9 9
    9 2 2 9
    9 5 5 9
    9 9 9 9
  • Re: [C] Problema lettura da stringhe

    Dunque...

    Per come la vedo io che non sono un esperto, usi un buffer troppo "piccolo". Dico questo perché ogni riga del tuo file contiene sette caratteri (quattro cifre più tre spazi), ai quali occorre aggiungere il terminatore che fgets() aggiunge per "chiudere" la stringa letta. Arrivi così a otto caratteri, che è la capacità massima del buffer, MA occorre tenere presente che fscanf() completa davvero il suo compito se ha la possibilità di aggiungere anche un carattere '\n' PRIMA del terminatore. Se lo spazio non è sufficiente, il '\n' non viene scartato ma resta "pendente" nello stream, il che può dare problemi. Puoi superare questo ostacolo semplicemente con un buffer più capace, tipo char buffer[16], quindi eliminando il '\n' dalla stringa.
    size_t l;
    char buffer[16];
    
    /* ... */
    
    while(fgets(buffer, sizeof(buffer), fp) != NULL) {
        // elimina '\n' alla fine della stringa, se c'e'
        l = strlen( buffer );
            
        if( '\n' == buffer[l-1] )
            buffer[--l] = '\0';
        else ultima_riga = 1; // un flag che segnala il raggiungimento
                              // della fine dei dati (la fine del file)
        /* ... */
    Un'altra cosa che mi sembra non vada è il modo in cui usi sscanf(), perché non legge l'intero buffer ma si ferma al primo carattere spaziatore. A mio avviso sarebbe più sensato copiare il buffer con strncpy() direttamente nella struttura che hai allocato (rendendo superfluo tmp).
    strncpy( v[i++].cod, buffer, 8 );
    La parte successiva, quella dove verificavi che x==1 e "consolidavi" tmp in v per poi incrementare i, diventa superflua.

    Quando ultima_riga viene impostato su 1, allora è ora di uscire dal ciclo, ad esempio con un break.

    Riassumendo:

    elenco_codici leggi_dati(const char *nome_file) {
        /* Apertura file, dichiarazioni di variabili */
        FILE *fp = fopen(nome_file, "r");
        int i = 0;
        int n = MAX_PARTITE;
        int ultima_riga = 0;
        char buffer[16];
        size_t l;
    
        codice *v = malloc(sizeof(codice)*n);
    
        while(fgets(buffer, sizeof(buffer), fp) != NULL) {
            // elimina '\n' alla fine della stringa, se c'e'
            l = strlen( buffer );
    
            if( '\n' == buffer[l-1] )
                buffer[--l] = '\0';
            else ultima_riga = 1; // un flag che segnala il raggiungimento
                                  // della fine dei dati (la fine del file)
    
            strncpy( v[i++].cod, buffer, 8 );
    
            if( ultima_riga ) break;
        }
    
        /* il resto non l'ho toccato */
    Come sempre, in maiuscolo e grassetto: NON SONO UN PROGRAMMATORE, BENSI' UN HOBBISTA AUTODIDATTA, QUEL CHE FACCIO LO FACCIO PER DIVERTIMENTO E VA SEMPRE PRESO CON LE PINZE E VERIFICATO. Se gli esperti vedono delle corbellerie, mi correggano, così imparo anch'io qualcosa in più. Grazie.
  • Re: [C] Problema lettura da stringhe

    Allora... Innanzitutto ho guardato tutte le risposte e vi ringrazio per l'attenzione, in particolare non avevo pensato alla strcpy. Sicuramente io non avrò niente da dire sui suggerimenti, sono l'ultimo a poter parlare
    Ho finalmente risolto modificando la struttura e inserendo 4 interi, perché mi sono reso conto che una richiesta successiva necessitava dei codici per fare verifiche sulle cifre crescenti e dunque mi son portato avanti.
    Per completezza posto il codice per intero (ho aggiunto ancora un paio di funzioncine).
    #include <assert.h>		//Contiene la macro assert, utilizzata per identificare errori logici ed altri tipi di bug nelle versioni di debug di un programma.
    #include <complex.h>	//Un gruppo di funzioni usate per manipolare numeri complessi. (Aggiunto con il C99)
    #include <ctype.h>		//Questo header file contiene funzioni usate per classificare i caratteri in base ai loro tipi o per convertirli da maiuscoli a minuscoli, indipendentemente dal 							set di caratteri utilizzato (tipicamente ASCII, ma esistono anche implementezioni che usano l'EBCDIC).
    #include <errno.h>		//Per testare i codici di errore restituiti dalle funzioni di libreria.
    #include <fenv.h>		//Per controllare l'ambiente in virgola mobile. (Aggiunto con il C99)
    #include <float.h>		//Contiene delle costanti definite che indicano le proprietà specifiche dell'implementazione della libreria in virgola mobile, come ad esempio la minima 							differenza tra due numeri in virgola mobile (_EPSILON), il massimo numero di cifre significative (_DIG) e l'intervallo di numeri che possono essere rappresentati 							(_MIN, _MAX).
    #include <inttypes.h>	//Per effettuare conversioni precise tra i tipi interi. (Aggiunto con il C99)
    #include <iso646.h>		//Per programmare nel set di caratteri ISO 646. (Aggiunto con l'NA1)
    #include <limits.h>		//Contiene delle costanti definite che indicano le proprietà specifiche dell'implementazione dei tipi interi, come l'intervallo dei numeri rappresentabili (_MIN, 							_MAX).
    #include <locale.h>		//Per setlocale() e le costanti relative. Utilizzato per scegliere il codice locale adatto.
    #include <math.h>		//Per le funzioni matematiche comuni.
    #include <setjmp.h>		//Dichiara setjmp/longjmp, utilizzate per salti non locali.
    #include <signal.h>		//Per controllare varie condizioni d'eccezione.
    #include <stdarg.h>		//Utilizzato da funzioni che accettano un numero variabile di parametri.
    #include <stdbool.h>	//Per un tipo di dato booleano. (Aggiunto con il C99)
    #include <stdint.h>		//Per definire vari tipi interi. (Aggiunto con il C99)
    #include <stddef.h>		//Per definire vari tipi e macro utili.
    // #include <stdio.h>		//Fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo delle venerabili funzioni printf e scanf.
    // #include <stdlib.h>		//Per eseguire un gran numero di operazioni, tra cui conversioni, generazione di numeri pseudo-casuali, allocazione di memoria, controllo del processo, variabili 							d'ambiente, segnali, ricerca ed ordinamento.
    // #include <string.h>		//Per manipolare le stringhe.
    
    #include <tgmath.h>		//Per funzioni matematiche di tipo generico. (Aggiunto con il C99)
    #include <time.h>		//Per convertire tra vari formati di data e ora.
    #include <wchar.h>		//Per manipolare stream o stringhe contenenti caratteri estesi - fondamentale per supportare una lunga serie di lingue con caratteri non occidentali. (Aggiunto 						con l'NA1)
    #include <wctype.h>		//Per la classificazione dei caratteri estesi. (Aggiunto con l'NA1)
    
    #include <stdio.h>		//Fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo delle venerabili funzioni printf e scanf.
    #include <stdlib.h>		//Per eseguire un gran numero di operazioni, tra cui conversioni, generazione di numeri pseudo-casuali, allocazione di memoria, controllo del processo, variabili 							d'ambiente, segnali, ricerca ed ordinamento.
    #include <string.h>		//Per manipolare le stringhe.
    
    #define CIFRE_CODICE 4
    #define MAX_TENTATIVI 5
    #define PARTITE 6
    #define MAX_PARTITE (6 * (5 + 1))
    
    /* Strutture usate */
    
    typedef struct CODICE {
    	int a;
    	int b;
    	int c;
    	int d;
    	// int num_giocatore;
    	char tipo;				 // 'C' se è un codice da indovinare, 'T' se è un codice scritto dal decodificatore e quindi si tratta di un tentativo
    } codice;
    
    /* Vettore di strutture (*v) con dimensione (n) */
    typedef struct ELENCO_CODICI {
    	codice *v;
    	int n;
    } elenco_codici;
    
    
    /* Funzioni usate */
    
    /* Funzione che legge i dati e li sistema bene nella struttura partita */
    
    elenco_codici leggi_dati(char *argv[]) {
    	
    	/* Apertura file, dichiarazioni di variabili */
    	FILE *fp;
    	fp = fopen(argv[1], "r");
    	char buffer[8];
    	int i = 0;
    	int x;
    	int n = MAX_PARTITE;
    	codice tmp;
    	codice *v = malloc(sizeof(codice)*n);
    	
    	/* C'è un errore */
    	while(fgets(buffer, sizeof(buffer), fp) != NULL) {
    		
    		x = sscanf(buffer, "%d %d %d %d", &tmp.a, &tmp.b, &tmp.c, &tmp.d);
    		if (x == 4) {
    			v[i] = tmp;
    			i++;
    		}
    	}
    	
    	n = i;
    	v = realloc(v, sizeof(codice)*n);
    	fclose(fp);
    	
    //	/* Stampa di prova */
    //	int k;
    //	for (k = 0; k < n; k++) {
    //			printf("%d %d %d %d\n", v[k].a, v[k].b, v[k].c, v[k].d);
    //	}
    	
    	/* Struttura elenco codici, con dentro l'elenco e il numero di codici totali */
    	elenco_codici E;
    	E.v = v;
    	E.n = n;
    	return E;
    }
    
    
    /* Funzione che analizza un codice: se è da indovinare assegna 'C' al campo "tipo"
       della struct CODICE, altrimenti gli assegna 'T' (tentativo). */
       
    void iscode_ortry(codice *v, int n) {
    	
    	int i;
    	int j = 1;
    	int k;
    	
    	/* All'inizio sono tutti C */
    	for (i = 0; i < n; i++)
    		v[i].tipo = 'C';
    	
    	i = 0;
    	
    	/* A ogni giro del ciclo, questo while salta al prossimo codice da indovinare... O almeno dovrebbe */
    	while (i < n) {
    		
    		k = 0;
    		
    		// ERRORE CHE NON RIESCO A SISTEMARE SU C O T
    		/* Finché i codici sono diversi e ci troviamo entro i 5 tentativi, allora il codice j-esimo è un tentativo T */
    		while (k == 0 && j < (i + MAX_TENTATIVI + 1) ) {
    			if (v[i].a != v[j].a || v[i].b != v[j].b || v[i].c != v[j].c || v[i].d != v[j].d) {	
    				k = 0;
    			} else {
    				k = 1;
    			}
    			
    			v[j].tipo = 'T';
    			j++;
    			
    			/* Stampa di prova */
    			//printf("%c\n", v[j].tipo);
    		}
    		i = j;
    		j++;
    	}
    	
    //	/* Stampa di prova */
    //	for (i = 0; i < n; i++) {
    //		printf("%d %d %d %d %c\n\n", v[i].a, v[i].b, v[i].c, v[i].d, v[i].tipo);
    //	}
    }
    
    /* Funzione che fa la media dei tentativi effettuati dai giocatori */
    
    void media_tentativi(codice *v, int n) {
    	int i;
    	int k = 0;
    	double media;
    	
    	for (i = 0; i < n; i++) {
    		if (v[i].tipo != 'C') {
    			k++;
    		}
    	}
    	
    	media = (double) k / PARTITE;
    	printf("[MEDIA_TENTATIVI]\n");
    	printf("%1.1f\n", media);
    }
    
    
    int main(int argc, char *argv[]) {
    	
    	elenco_codici E;
    	E = leggi_dati(argv);
    	iscode_ortry(E.v, E.n);
    	
    	/* Richiesta 1 */
    	media_tentativi(E.v, E.n);
    	
    	
    	return 0;
    }
    
    
    
    
    Grazie ancora per il supporto!

    PS: non fate caso alla lista di direttive lassù, è un elenco che mi tengo per me.
  • Re: [C] Problema lettura da stringhe

    In effetti mi chiedevo perché mai volessi immagazzinare la stringa anziché i quattro valori convertiti, però ho voluto seguire la tua "linea" pensando che potessi avere ragioni "recondite". Non ne avevi!
Devi accedere o registrarti per scrivere nel forum
7 risposte