web-dev-qa-db-fra.com

Bonne façon de lire un fichier texte dans un tampon en C?

Je traite de petits fichiers texte que je veux lire dans un tampon pendant que je les traite, donc j'ai trouvé le code suivant:

...
char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}
...

Est-ce la bonne façon de mettre le contenu du fichier dans le tampon ou est-ce que j'abuse strcat()?

J'itère ensuite à travers le tampon ainsi:

for(int x = 0; (c = source[x]) != '\0'; x++)
{
    //Process chars
}
24
Gary Willoughby
char source[1000000];

FILE *fp = fopen("TheFile.txt", "r");
if(fp != NULL)
{
    while((symbol = getc(fp)) != EOF)
    {
        strcat(source, &symbol);
    }
    fclose(fp);
}

Il y a pas mal de problèmes avec ce code:

  1. C'est très lent (vous extrayez le tampon un caractère à la fois).
  2. Si la taille du fichier est supérieure à sizeof(source), cela est sujet à des débordements de tampon.
  3. Vraiment, quand on y regarde de plus près, ce code ne devrait pas fonctionner du tout. Comme indiqué dans les pages de manuel:

La fonction strcat() ajoute une copie de la chaîne terminée par un caractère nul s2 à la fin de la chaîne terminée par un caractère nul s1, puis ajoute un "\ 0" se terminant.

Vous ajoutez un caractère (pas une chaîne terminée par NUL!) À une chaîne qui peut ou non se terminer par NUL. Le niquement fois que je peux imaginer que cela fonctionne selon la description de la page de manuel est si chaque caractère du fichier se termine par NUL, auquel cas ce serait plutôt inutile. Alors oui, c'est certainement un terrible abus de strcat().

Voici deux alternatives à envisager à la place.

Si vous connaissez à l'avance la taille maximale du tampon:

#include <stdio.h>
#define MAXBUFLEN 1000000

char source[MAXBUFLEN + 1];
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp);
    if ( ferror( fp ) != 0 ) {
        fputs("Error reading file", stderr);
    } else {
        source[newLen++] = '\0'; /* Just to be safe. */
    }

    fclose(fp);
}

Ou, si vous ne le faites pas:

#include <stdio.h>
#include <stdlib.h>

char *source = NULL;
FILE *fp = fopen("foo.txt", "r");
if (fp != NULL) {
    /* Go to the end of the file. */
    if (fseek(fp, 0L, SEEK_END) == 0) {
        /* Get the size of the file. */
        long bufsize = ftell(fp);
        if (bufsize == -1) { /* Error */ }

        /* Allocate our buffer to that size. */
        source = malloc(sizeof(char) * (bufsize + 1));

        /* Go back to the start of the file. */
        if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if ( ferror( fp ) != 0 ) {
            fputs("Error reading file", stderr);
        } else {
            source[newLen++] = '\0'; /* Just to be safe. */
        }
    }
    fclose(fp);
}

free(source); /* Don't forget to call free() later! */
68
Michael

Oui - vous seriez probablement arrêté pour votre terrible abus de strcat!

Jetez un œil à getline (), il lit les données ligne par ligne, mais surtout, il peut limiter le nombre de caractères que vous lisez, afin de ne pas déborder le tampon.

Strcat est relativement lent car il doit rechercher la fin de la chaîne entière à chaque insertion de caractère. Vous devriez normalement garder un pointeur vers la fin actuelle du stockage de chaîne et le passer à getline comme position de lecture de la ligne suivante.

4
Martin Beckett

Voir cet article de JoelOnSoftware pour savoir pourquoi vous ne voulez pas utiliser strcat.

Regardez fread pour une alternative. Utilisez-le avec 1 pour la taille lorsque vous lisez des octets ou des caractères.

1
Mark Ransom

Pourquoi n'utilisez-vous pas simplement le tableau de caractères que vous avez? Cela devrait le faire:

   source[i] = getc(fp); 
   i++;
1
Martin Wickman

Si vous êtes sur un système Linux, une fois que vous avez le descripteur de fichier, vous pouvez obtenir beaucoup d'informations sur le fichier en utilisant fstat ()

http://linux.die.net/man/2/stat

donc vous pourriez avoir

#include  <unistd.h> 
void main()
{
    struct stat stat;
    int fd;
    //get file descriptor
    fstat(fd, &stat);
    //the size of the file is now in stat.st_size
}

Cela évite de chercher au début et à la fin du fichier.

1
toweleeele

Non testé, mais devrait fonctionner .. Et oui, il pourrait être mieux implémenté avec fread, je laisse cela comme un exercice au lecteur.

#define DEFAULT_SIZE 100
#define STEP_SIZE 100

char *buffer[DEFAULT_SIZE];
size_t buffer_sz=DEFAULT_SIZE;
size_t i=0;
while(!feof(fp)){
  buffer[i]=fgetc(fp);
  i++;
  if(i>=buffer_sz){
    buffer_sz+=STEP_SIZE;
    void *tmp=buffer;
    buffer=realloc(buffer,buffer_sz);
    if(buffer==null){ free(tmp); exit(1);} //ensure we don't have a memory leak
  }
}
buffer[i]=0;
1
Earlz

Il me semble que vous voulez faire peur:

http://www.cplusplus.com/reference/clibrary/cstdio/fread/

0
wprl