Leer una línea del archivo en C y extraer el número de entrada
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 12
en 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
el ciclo while (sscanf(line, "%d ", &number))
sigue analizando el primer número de la línea.
Deberías usar strtol
en 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;
}
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 line
que 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 stdin
forma 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.
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 int
al 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;
}