web-dev-qa-db-fra.com

Programmation C: Comment lire tout le contenu du fichier dans un tampon

Je veux écrire le contenu complet d'un fichier dans un tampon. Le fichier ne contient en réalité qu'une chaîne que je dois comparer à une chaîne.

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

ENV: Windows

70
Sunny

La portabilité entre Linux et Windows est un gros casse-tête, car Linux est un système conforme à POSIX avec - en général - 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 vous en tenir à 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, 1, fsize, 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 par 0. Ce code est juste du standard C, ce n'est pas spécifique à POSIX (bien que cela ne garantisse pas que cela fonctionnera/compilera sous Windows ...)

136
user529758

Voici ce que je recommanderais.

Il devrait être conforme à la norme C89 et être complètement portable. En particulier, cela fonctionne aussi sur les tuyaux et les sockets sur les systèmes POSIXy.

L'idée est que nous lisons l'entrée sous forme de gros morceaux (READALL_CHUNK), en réaffectant dynamiquement le tampon selon nos besoins. Nous utilisons uniquement 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 bloc constante, READALL_CHUNK == 262144 (256*1024). Cela signifie que dans le pire des cas, jusqu'à 262145 caractères sont perdus (alloués mais non utilisés), mais seulement temporairement. A la fin, la fonction réalloue le tampon à la taille optimale. En outre, cela signifie que nous effectuons quatre réaffectations par mégaoctet de données lues.

La valeur par défaut de 262144 octets dans le code ci-dessus est une valeur conservatrice; cela fonctionne bien, même pour les anciens minilaptops et Raspberry Pis, ainsi que pour la plupart des périphériques intégrés avec au moins quelques mégaoctets de RAM disponibles pour le processus. Cependant, il n’est pas si petit que cela ralentisse l’opération (en raison de nombreux appels en lecture et de nombreuses réaffectations de mémoire tampon) sur la plupart des systèmes.

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

Comme la définition de READALL_CHUNK est gardée (c’est-à-dire qu’elle n’est définie qu’à ce stade du code), vous pouvez remplacer la valeur par défaut à la compilation en utilisant (dans la plupart des compilateurs C) -DREADALL_CHUNK=2097152 option de ligne de commande - mais vérifiez bien les options de votre compilateur pour définir une macro de préprocesseur à l'aide des options de ligne de commande.

17
Nominal Animal