Leggi una riga dal file in C ed estrai il numero di input

Aug 17 2020

Ho un file input.dat . In questo file ci sono 3 righe:

1 2 3
5 7 10 12
8 9 14 13 15 17

Leggerò una delle tre righe usando C e restituirò il numero degli elementi. Ad esempio, voglio leggere la seconda riga 5 7 10 12in memoria e restituire anche il numero di valori nella seconda riga, che è 4. Il mio codice è sotto ...

#include <stdio.h>
#include <stdlib.h>

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat", "r");
    char line[STRING_SIZE];
    int lcount = 0, nline = 1, sum = 0, number;

    if (fp != NULL) {
        while (fgets(line, STRING_SIZE, fp) != NULL) {
            if (lcount == nline) {
                while (sscanf(line, "%d ", &number)) {
                    sum++;
                }
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    exit(0);
}

Quando eseguo questo codice, non si ferma mai come un ciclo morto. Qual è il problema qui?

Risposte

1 chqrlie Aug 17 2020 at 19:57

il ciclo while (sscanf(line, "%d ", &number))continua ad analizzare il primo numero nella riga.

Dovresti strtolinvece usare :

#include <stdio.h>
#include <stdlib.h>

#define STRING_SIZE 2000

int main() {
    FILE *fp = fopen("in.dat", "r");
    char line[STRING_SIZE];
    int lcount = 0, nline = 1;

    if (fp != NULL) {
        while (fgets(line, STRING_SIZE, fp) != NULL) {
            if (lcount == nline) {
                char *p = line, *q;
                int count = 0;
                for (;;) {
                    long val = strtol(p, &q, 0);    // parse an integer
                    if (q == p) {
                        // end of string or not a number
                        break;
                    }
                    // value was read into val. You can use it for whatever purpose
                    count++;
                    p = q;
                }
                printf("%d\n", count);
                break;
            } else {
                lcount++;
            }
        }
        fclose(fp);
    }
    return 0;
}
1 DavidC.Rankin Aug 17 2020 at 22:05

Stavi pensando lungo il percorso giusto con il tuo utilizzo sscanf(), l'unico pezzo del puzzle che ti mancava è come applicare un offset in linemodo da leggere il valore successivo nella riga con la tua prossima chiamata a sscanf(). Puoi farlo tenendo traccia del numero di caratteri consumati su ciascuna chiamata per sscanf()utilizzare la "%n"conversione (non si aggiunge al conteggio delle conversioni restituito da sscanf()) Ad esempio leggendo le righe dal flusso di file aperto fp, potresti fare:

#define MAXC  1024      /* if you need a constant, #define one (or more) */
...
    char line[MAXC] = "";   /* buffer to hold each line */
    ...
    while (fgets (line, MAXC, fp)) {    /* reach each line in file */
        int offset = 0,                 /* offset in line for next sscanf() read */
            nchr = 0,                   /* number of char consumed by last read */
            val,                        /* integer value read with sscanf() */
            nval = 0;                   /* number of values read in line */
        /* conververt each integer at line + offset, saving no. of chars consumed */
        while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
            printf (" %d", val);        /* output value read */
            offset += nchr;             /* update offset with no. chars consumend */
            nval++;                     /* increment value count */
        }
        printf ("  -  %d values\n", nval);  /* output no. values in line */
    }

( Nota: strtol() fornisce una migliore segnalazione degli errori rispetto sscanf()a una conversione non riuscita)

Se lo metti insieme a un esempio che legge dal nome del file fornito come primo argomento al programma (o legge stdinda predefinito se non viene fornito alcun argomento), potresti fare:

#include <stdio.h>

#define MAXC  1024      /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {

    char line[MAXC] = "";   /* buffer to hold each line */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (line, MAXC, fp)) {    /* reach each line in file */
        int offset = 0,                 /* offset in line for next sscanf() read */
            nchr = 0,                   /* number of char consumed by last read */
            val,                        /* integer value read with sscanf() */
            nval = 0;                   /* number of values read in line */
        /* conververt each integer at line + offset, saving no. of chars consumed */
        while (sscanf (line + offset, "%d%n", &val, &nchr) == 1) {
            printf (" %d", val);        /* output value read */
            offset += nchr;             /* update offset with no. chars consumend */
            nval++;                     /* increment value count */
        }
        printf ("  -  %d values\n", nval);  /* output no. values in line */
    }

    if (fp != stdin)                    /* close file if not stdin */
        fclose (fp);
}

Esempio di utilizzo / output

Con i dati che mostri nel nome del file dat/nvals.txtotterrai:

$ ./bin/fgetsnvals dat/nvals.txt
 1 2 3  -  3 values
 5 7 10 12  -  4 values
 8 9 14 13 15 17  -  6 values

Esamina le cose e fammi sapere se hai ulteriori domande.

1 RobinHellmers Aug 17 2020 at 21:41

Una versione un po 'più pulita della risposta di chqrlie . È iniziato con una stringa poiché è questo ciò che la domanda tratta veramente dopo fgets().

sscanf() non passa attraverso la stringa, legge sempre dall'inizio.

strtol()cerca un long intall'inizio della stringa, ignorando lo spazio bianco iniziale. Restituisce l'indirizzo del punto in cui interrompe la scansione.

Il manuale di strtol()dice che errno dovrebbe essere controllato per qualsiasi errore di conversione.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define STRING_SIZE 2000

int main(void)
{
    char line[STRING_SIZE] = "5 7 10 12";

    char* start = line;
    char* end;

    int count = 0;

    while(1)
    {
        /**
         * strtol() look for long int in beginning of the string
         * Ignores beginning whitespace
         * 
         * start: where to strtol() start looking for long int
         * end: where strtol() stops scanning for long int
         */
        errno = 0; // As strol() manual says

        strtol(start, &end, 0);

        if (errno != 0)
        {
            printf("Error in strtol() conversion.\n");
            exit(0);
        }

        if (start == end) break; // Quit loop

        start = end;
        count++;
    }
    

    printf("count: %d\n", count);

    return 0;
}