Leer una línea del archivo en C y extraer el número de entrada

Aug 17 2020

Tengo un archivo input.dat . En este archivo, hay 3 líneas:

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

Voy a leer una de las tres líneas usando C y devolveré el número de elementos. Por ejemplo, quiero leer la segunda línea 5 7 10 12en la memoria y también devolver el número de valores en la segunda línea, que es 4. Mi código está debajo ...

#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);
}

Cuando ejecuto este código, nunca se detiene como un bucle muerto. ¿Cuál es el problema aquí?

Respuestas

1 chqrlie Aug 17 2020 at 19:57

el ciclo while (sscanf(line, "%d ", &number))sigue analizando el primer número de la línea.

Deberías usar strtolen su lugar:

#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

Estaba pensando en el camino correcto con su uso de sscanf(), la única pieza del rompecabezas que le faltaba es cómo aplicar un desplazamiento para lineque lea el siguiente valor en la línea con su próxima llamada a sscanf(). Para ello, realice un seguimiento de la cantidad de caracteres consumidos en cada llamada para sscanf()usar la "%n"conversión (no se suma a su recuento de conversiones devuelto por sscanf()). Por ejemplo, al leer líneas del flujo de archivos abierto fp, puede hacer:

#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() proporciona mejores informes de errores que sscanf()en una conversión fallida)

Si lo pones junto con un ejemplo que lee desde el nombre de archivo proporcionado como primer argumento del programa (o lee de stdinforma predeterminada si no se proporciona ningún argumento), podrías hacer:

#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);
}

Ejemplo de uso / salida

Con los datos que muestra en el nombre del archivo dat/nvals.txt, obtendría:

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

Revise las cosas y avíseme si tiene más preguntas.

1 RobinHellmers Aug 17 2020 at 21:41

Una versión un poco más limpia de la respuesta de chqrlie . Comenzó con una cadena, ya que de eso se trata realmente la pregunta después fgets().

sscanf() no pasa por la cadena, siempre se lee desde el principio.

strtol()busca un long intal principio de la cadena, ignorando el espacio en blanco inicial. Devuelve la dirección de donde deja de escanear.

El manual de strtol()dice que se debe verificar errno para detectar cualquier error de conversión.

#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;
}