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.