100 votes

Programmation en C : Comment lire le contenu complet d'un fichier dans un tampon ?

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

Quelle serait l'option la plus efficace et portable même sous linux ?

ENV : Windows

197voto

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 le C, alors que Windows ne fournit même pas beaucoup de fonctions dans la bibliothèque standard du 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, 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 par 0. Ce code est juste du C standard, il n'est pas spécifique à POSIX (bien que cela ne garantisse pas qu'il fonctionnera/compilera sous Windows...).

37voto

Nominal Animal Points 7207

Voici ce que je recommande.

Il doit être conforme à la norme C89 et être entièrement portable. En particulier, il fonctionne également avec les pipes et les sockets 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 selon nos besoins. Nous utilisons seulement realloc() , fread() , ferror() y 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és (alloués mais non utilisés), mais seulement temporairement. À la fin, la fonction réalloue le tampon à la taille optimale. Cela signifie également 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 ; elle fonctionne bien, même pour les vieux mini-ordinateurs portables et les Raspberry Pis et la plupart des dispositifs embarqués avec au moins quelques mégaoctets de RAM disponibles pour le processus. Pourtant, elle n'est pas si petite qu'elle ralentit l'opération (en raison des nombreux appels de lecture et des nombreuses réallocations de tampon) sur la plupart des systèmes.

Pour les ordinateurs de bureau, à l'heure actuelle (2017), je recommande un disque dur beaucoup plus grand. READALL_CHUNK peut-être #define READALL_CHUNK 2097152 (2 MiB).

Parce que la définition de READALL_CHUNK est gardée (c'est-à-dire qu'elle n'est définie que si elle est encore indéfinie à ce moment-là du 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 mais vérifiez les options de votre compilateur pour définir une macro de préprocesseur en utilisant des options de ligne de commande.

-2voto

md5 Points 14957

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 ou s'il s'agit d'un grand nombre (tel que buffer serait trop grande pour tenir sur la pile), utilisez l'allocation dynamique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X