web-dev-qa-db-fra.com

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 déconcerté par les méthodes que C utilise pour lire dans un fichier. Tout ce que j'ai vraiment besoin de faire est de lire un fichier ligne par ligne et d’utiliser les informations recueillies à partir de chaque ligne pour effectuer quelques manipulations. J'ai essayé d'utiliser la méthode getline et d'autres sans succès. Mon code est actuellement le suivant:

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("%s\n", line);
      }
    return 1;
}

À l’heure actuelle, la méthode sscanf pose un problème grave et je ne sais pas pourquoi. Je suis un C noob total et je me demande juste s'il y avait quelque chose de grand qui me manquait. Merci

53
Dan Bradbury

Tant de problèmes en si peu de lignes. J'en oublie probablement quelques-uns:

  • argv [0] est le nom du programme, pas le premier argument;
  • si vous voulez lire une variable, vous devez allouer sa 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, utilisez fscanf,
  • "% s" s'arrêtera au premier espace en tant que format pour la famille? scanf
  • pour lire une ligne, la fonction standard est fgets,
  • renvoyer 1 de l'échec des moyens principaux

Alors

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

Pour lire une ligne d'un fichier, vous devez utiliser la fonction fgets: Elle lit une chaîne du fichier spécifié jusqu'à un caractère de nouvelle ligne ou EOF.

L'utilisation de sscanf dans votre code ne fonctionnerait pas du tout, puisque vous utilisez filename comme chaîne de format pour la lecture de line dans un littéral de chaîne constant %s.

La raison de SEGV est que vous écrivez dans la mémoire non allouée indiquée par line.

7
Abrixas2

Supposons que vous traitez avec un autre délimiteur, tel qu'un onglet _\t_, au lieu d'une nouvelle ligne _\n_.

Une approche plus générale des délimiteurs consiste à utiliser getc() , qui saisit un caractère à la fois.

Notez que getc() retourne un int, afin que nous puissions vérifier son é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 devons sauvegarder ce dernier caractère pour un _\0_ terminateur personnage).

L'utilisation d'un tableau évite d'avoir à utiliser 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] = '\0';
            fprintf(stdout, "%s\n", line);
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            fprintf(stdout, "%s\n", line);
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}
_

Si vous devez utiliser un _char *_, vous pouvez toujours utiliser ce code, mais vous strdup() le tableau _line[]_, une fois qu'il est rempli avec une valeur de ligne d'entrée. Vous devez free cette chaîne dupliquée une fois que vous avez terminé, ou vous obtiendrez une fuite de mémoire:

_#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] = '\0';
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            break;
        }
        else if (tempChar == '\n') {
            line[tempCharIdx] = '\0';
            tempCharIdx = 0U;
            dynamicLine = strdup(line);
            fprintf(stdout, "%s\n", dynamicLine);
            free(dynamicLine);
            dynamicLine = NULL;
            continue;
        }
        else
            line[tempCharIdx++] = (char)tempChar;
    }

    return EXIT_SUCCESS;
}
_
5
Alex Reynolds

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

4