Lire une ligne du fichier en C et extraire le nombre d'entrée

Aug 17 2020

J'ai un fichier input.dat . Dans ce fichier, il y a 3 lignes:

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

Je vais lire l'une des trois lignes en utilisant C et renvoyer le nombre d'éléments. Par exemple, je veux lire la 2ème ligne 5 7 10 12en mémoire et également renvoyer le nombre de valeurs dans la 2ème ligne, qui est 4. Mon code est ci-dessous ...

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

Lorsque j'exécute ce code, il ne s'arrête jamais comme une boucle morte. Quel est le problème ici?

Réponses

1 chqrlie Aug 17 2020 at 19:57

la boucle while (sscanf(line, "%d ", &number))continue d'analyser le premier nombre de la ligne.

Vous devriez utiliser à la strtolplace:

#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

Vous pensiez sur la bonne voie avec votre utilisation de sscanf(), la seule pièce du puzzle qui vous manquait est de savoir comment appliquer un décalage lineafin que vous lisiez la valeur suivante dans la ligne avec votre prochain appel à sscanf(). Vous faites cela en gardant une trace du nombre de caractères consommés à chaque appel à l' sscanf()utilisation de la "%n"conversion (cela ne s'ajoute pas à votre compte de conversion renvoyé par sscanf()) Par exemple, en lisant des lignes à partir du flux de fichiers ouvert fp, vous pouvez faire:

#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 */
    }

( Remarque: strtol() fournit un meilleur rapport d'erreur que sscanf()sur une conversion ayant échoué)

Si vous l'associez à un exemple qui lit le nom de fichier fourni comme premier argument du programme (ou lit stdinpar défaut si aucun argument n'est donné), vous pouvez faire:

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

Exemple d'utilisation / sortie

Avec les données que vous affichez dans le nom de fichier, dat/nvals.txtvous obtiendrez:

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

Regardez les choses et faites-moi savoir si vous avez d'autres questions.

1 RobinHellmers Aug 17 2020 at 21:41

Une version un peu plus propre de la réponse de chqrlie . Commencé avec une chaîne car c'est ce dont la question est vraiment après fgets().

sscanf() ne parcourra pas la chaîne, il lit toujours depuis le début.

strtol()recherche un long intau début de la chaîne, en ignorant l'espace blanc initial. Renvoie l'adresse de l'endroit où il arrête la numérisation.

Le manuel de strtol()indique que errno doit être vérifié pour toute erreur de conversion.

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