C Programmation: comment lire le contenu du fichier entier dans un tampon [dupliquer]

Cette question a déjà une réponse ici:

Je veux écrire le contenu complet d'un fichier dans un buffer. Le fichier ne contient en fait qu'une chaîne que je dois comparer avec une chaîne.

Quelle serait l'option la plus efficace qui soit portable même sur Linux.

ENV: Windows

57
demandé sur Sunny 2012-12-22 16:45:49

4 réponses

La portabilité entre Linux et Windows est un gros casse-tête, puisque Linux est un système conforme à POSIX avec-généralement - une chaîne d'outils appropriée et de haute qualité pour C, alors que Windows ne fournit même pas beaucoup de fonctions dans la bibliothèque standard C.

Cependant, si vous voulez coller à la norme, vous pouvez écrire quelque chose comme ceci:

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

FILE *f = fopen("textfile.txt", "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);  //same as rewind(f);

char *string = malloc(fsize + 1);
fread(string, fsize, 1, f);
fclose(f);

string[fsize] = 0;

Ici string contiendra le contenu du fichier texte sous la forme d'une chaîne c correctement terminée à 0. Ce code est juste standard C, ce n'est pas Spécifique à POSIX (bien que cela ne garantisse pas qu'il fonctionnera / compilera sur Windows...)

107
répondu Enkelli 2016-04-14 16:02:16

Voici ce que je recommanderais.

Il devrait être conforme à C89, et être complètement portable. En particulier, il fonctionne également sur les tuyaux et les prises sur les systèmes POSIXy.

L'idée est que nous lisons l'entrée en gros morceaux (READALL_CHUNK), en réallouant dynamiquement le tampon comme nous en avons besoin. Nous n'utilisons que des realloc(), fread(), ferror(), et free():

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

/* Size of each input chunk to be
   read and allocate for. */
#ifndef  READALL_CHUNK
#define  READALL_CHUNK  262144
#endif

#define  READALL_OK          0  /* Success */
#define  READALL_INVALID    -1  /* Invalid parameters */
#define  READALL_ERROR      -2  /* Stream error */
#define  READALL_TOOMUCH    -3  /* Too much input */
#define  READALL_NOMEM      -4  /* Out of memory */

/* This function returns one of the READALL_ constants above.
   If the return value is zero == READALL_OK, then:
     (*dataptr) points to a dynamically allocated buffer, with
     (*sizeptr) chars read from the file.
     The buffer is allocated for one extra char, which is NUL,
     and automatically appended after the data.
   Initial values of (*dataptr) and (*sizeptr) are ignored.
*/
int readall(FILE *in, char **dataptr, size_t *sizeptr)
{
    char  *data = NULL, *temp;
    size_t size = 0;
    size_t used = 0;
    size_t n;

    /* None of the parameters can be NULL. */
    if (in == NULL || dataptr == NULL || sizeptr == NULL)
        return READALL_INVALID;

    /* A read error already occurred? */
    if (ferror(in))
        return READALL_ERROR;

    while (1) {

        if (used + READALL_CHUNK + 1 > size) {
            size = used + READALL_CHUNK + 1;

            /* Overflow check. Some ANSI C compilers
               may optimize this away, though. */
            if (size <= used) {
                free(data);
                return READALL_TOOMUCH;
            }

            temp = realloc(data, size);
            if (temp == NULL) {
                free(data);
                return READALL_NOMEM;
            }
            data = temp;
        }

        n = fread(data + used, 1, READALL_CHUNK, in);
        if (n == 0)
            break;

        used += n;
    }

    if (ferror(in)) {
        free(data);
        return READALL_ERROR;
    }

    temp = realloc(data, used + 1);
    if (temp == NULL) {
        free(data);
        return READALL_NOMEM;
    }
    data = temp;
    data[used] = '\0';

    *dataptr = data;
    *sizeptr = used;

    return READALL_OK;
}

Ci-dessus, j'ai utilisé une taille de morceau constante, READALL_CHUNK == 262144 (256*1024). Cela signifie que, dans le pire des cas, jusqu'à 262145 caractères sont gaspillées (attribuées mais non utilisé), mais seulement temporairement. À la fin, la fonction réalloue le tampon à la taille optimale. En outre, cela signifie que nous effectuons quatre réallocations par mégaoctet de données lues.

La valeur par défaut de 262144 octets dans le code ci-dessus est une valeur conservatrice; il fonctionne bien pour même les vieux Minilaptops et Raspberry Pis et la plupart des périphériques embarqués avec au moins quelques mégaoctets de RAM disponibles pour le processus. Pourtant, il n'est pas si petit qu'il ralentit l'opération (en raison de nombreux lire appels, et de nombreuses réallocations de tampon) sur la plupart des systèmes.

Pour les machines de bureau en ce moment (2017), je recommande un READALL_CHUNK beaucoup plus grand, peut-être #define READALL_CHUNK 2097152 (2 MiB).

Étant donné que la définition de READALL_CHUNK est gardée (c'est-à-dire qu'elle n'est définie que si elle n'est pas définie à ce moment-là dans le code), vous pouvez remplacer la valeur par défaut au moment de la compilation, en utilisant (dans la plupart des compilateurs C) -DREADALL_CHUNK=2097152 option de ligne de commande - mais vérifiez options de ligne de commande.

8
répondu Nominal Animal 2017-07-03 23:22:24

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

#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 (newLen == 0) {
        fputs("Error reading file", stderr);
    } else {
        source[newLen] = '\0'; /* Just to be safe. */
    }

    fclose(fp);
}

Ou, si vous ne le connaissez 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) { /* Handle error here */ }

        /* Read the entire file into memory. */
        size_t newLen = fread(source, sizeof(char), bufsize, fp);
        if (newLen == 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! */
6
répondu harikrishnan.n0077 2018-09-08 10:01:50

Une solution portable pourrait utiliser getc.

#include <stdio.h>

char buffer[MAX_FILE_SIZE];
size_t i;

for (i = 0; i < MAX_FILE_SIZE; ++i)
{
    int c = getc(fp);

    if (c == EOF)
    {
        buffer[i] = 0x00;
        break;
    }

    buffer[i] = c;
}

Si vous ne voulez pas avoir un MAX_FILE_SIZE macro ou si c'est un grand nombre (tel que buffer serait grande pour tenir sur la pile), l'utilisation de l'allocation dynamique.

-4
répondu md5 2012-12-22 12:50:08