Parcourir un fichier texte ligne par ligne en C

j'ai travaillé sur un petit exercice pour ma classe de SiC et je suis très confus par les méthodes utilisées par C pour lire un fichier. Tout ce que je vraiment besoin de faire est de lire un fichier ligne par ligne et d'utiliser les informations recueillies à partir de chaque ligne pour faire quelques manipulations. J'ai essayé la méthode getline et d'autres sans succès. Mon code est actuellement comme suit:

int main(char *argc, char* argv[]){
      const char *filename = argv[0];
      FILE *file = fopen(filename, "r");
      char *line = NULL;

      while(!feof(file)){
        sscanf(line, filename, "%s");
        printf("%sn", line);
      }
    return 1;
}

en ce moment, j'ai un problème avec la méthode sscanf et je ne sais pas pourquoi. Je suis un total C noob et je me demandais juste s'il y avait quelque chose d'important que je manquais. Merci

42
demandé sur Dan Bradbury 2012-02-09 09:57:37

4 réponses

beaucoup de problèmes en si peu de lignes. J'ai sans doute oublier certains:

  • argv[0] est le nom du programme, pas le premier argument;
  • si vous souhaitez lire dans une variable, vous devez allouer de la mémoire
  • on ne boucle jamais sur feof, on boucle sur une fonction IO jusqu'à ce qu'elle échoue, feof sert alors à déterminer la raison de l'échec,
  • sscanf est là pour analyser une ligne, si vous voulez analyser un fichier, utiliser fscanf,
  • "%s" s'arrêtera à la premier espace comme format pour le ?famille scanf
  • pour lire une ligne, la fonction standard est fgets,
  • retour 1 à partir des principaux moyens d'échec

#include <stdio.h>

int main(int argc, char* argv[])
{
    char const* const fileName = argv[1]; /* should check that argc > 1 */
    FILE* file = fopen(fileName, "r"); /* should check the result */
    char line[256];

    while (fgets(line, sizeof(line), file)) {
        /* note that fgets don't strip the terminating \n, checking its
           presence would allow to handle lines longer that sizeof(line) */
        printf("%s", line); 
    }
    /* may check feof here to make a difference between eof and io failure -- network
       timeout for instance */

    fclose(file);

    return 0;
}
108
répondu AProgrammer 2014-10-10 14:21:42

Pour lire une ligne d'un fichier, vous devez utiliser le fgets fonction: elle lit une chaîne à partir du fichier spécifié jusqu'à un caractère newline ou EOF.

sscanf dans votre code ne fonctionnerait pas du tout, tant que vous utilisez filename comme votre chaîne de format pour la lecture de line dans une chaîne constante littérale%s.

la raison de SEGV est que vous écrivez dans la mémoire non attribuée pointée par line.

7
répondu Abrixas2 2012-02-09 06:11:55

dites que vous avez affaire à un autre délimiteur, tel qu'un \t onglet, au lieu d'un \n retour à la ligne.

Une approche plus générale des délimiteurs est l'utilisation de getc(), qui saisit un caractère à la fois.

Notez que getc() retourne un int, de sorte que nous pouvons tester l'égalité avec EOF.

Deuxièmement, nous définissons un tableau line[BUFFER_MAX_LENGTH] de type char, afin de stocker jusqu'à BUFFER_MAX_LENGTH-1 caractères sur la pile (nous avons pour sauver le dernier caractère pour un caractère de terminaison).

Utilisation d'un tableau permet d'éviter l'utilisation malloc et free pour créer un pointeur de caractère de la bonne longueur sur le tas.

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    /* get a character from the file pointer */
    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '';
            fprintf(stdout, "%s\n", line);
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '';
            tempCharIdx = 0U;
            fprintf(stdout, "%s\n", line);
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}

Si vous devez utiliser un char *, alors vous pouvez toujours utiliser ce code, mais vous strdup()line[] array, une fois qu'il est rempli avec la valeur d'entrée d'une ligne. Vous devez vous free cette chaîne dupliquée une fois que vous en aurez fini avec elle, ou vous obtiendrez une mémoire fuite:

#define BUFFER_MAX_LENGTH 1024

int main(int argc, char* argv[])
{
    FILE *file = NULL;
    char line[BUFFER_MAX_LENGTH];
    int tempChar;
    unsigned int tempCharIdx = 0U;
    char *dynamicLine = NULL;

    if (argc == 2)
         file = fopen(argv[1], "r");
    else {
         fprintf(stderr, "error: wrong number of arguments\n"
                         "usage: %s textfile\n", argv[0]);
         return EXIT_FAILURE;
    }

    if (!file) {
         fprintf(stderr, "error: could not open textfile: %s\n", argv[1]);
         return EXIT_FAILURE;
    }

    while(tempChar = fgetc(file))
    {
        /* avoid buffer overflow error */
        if (tempCharIdx == BUFFER_MAX_LENGTH) {
            fprintf(stderr, "error: line is too long. increase BUFFER_MAX_LENGTH.\n");
            return EXIT_FAILURE;
        }

        /* test character value */
        if (tempChar == EOF) {
            line[tempCharIdx] = '';
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '';
            tempCharIdx = 0U;
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}
4
répondu Alex Reynolds 2012-02-10 22:00:00

en plus des autres réponses, sur une bibliothèque C récente (conforme à Posix 2008), vous pouvez utiliser getline. Voir cette réponse (pour une question connexe).

3
répondu Basile Starynkevitch 2017-05-23 11:33:24