136 votes

Comment lire le contenu d'un fichier dans une chaîne de caractères en C ?

Quelle est la manière la plus simple (la moins sujette aux erreurs, le moins de lignes de code, quelle que soit l'interprétation que vous voulez en faire) d'ouvrir un fichier en C et de lire son contenu dans une chaîne de caractères (char*, char[], etc.) ?

183voto

Nils Pipenbrinck Points 41006

J'ai tendance à charger l'intégralité du tampon en tant que morceau de mémoire brute dans la mémoire et à effectuer l'analyse syntaxique par mes propres moyens. De cette façon, j'ai le meilleur contrôle sur ce que fait la librairie standard sur plusieurs plateformes.

C'est un stub que j'utilise pour cela. Vous pouvez aussi vérifier les codes d'erreur pour fseek, ftell et fread. (omis pour plus de clarté).

char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");

if (f)
{
  fseek (f, 0, SEEK_END);
  length = ftell (f);
  fseek (f, 0, SEEK_SET);
  buffer = malloc (length);
  if (buffer)
  {
    fread (buffer, 1, length, f);
  }
  fclose (f);
}

if (buffer)
{
  // start to process your data / extract strings here...
}

41voto

Jeff Mc Points 1741

Une autre solution, malheureusement très dépendante du système d'exploitation, consiste à mapper le fichier en mémoire. Les avantages comprennent généralement la performance de la lecture et une utilisation réduite de la mémoire, car la vue des applications et le cache du fichier du système d'exploitation peuvent réellement partager la mémoire physique.

Le code POSIX ressemblerait à ceci :

int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);

Windows d'autre part est un peu plus délicat, et malheureusement je n'ai pas de compilateur devant moi pour le tester, mais la fonctionnalité est fournie par CreateFileMapping() y MapViewOfFile() .

21voto

dmityugov Points 2786

Si "lire son contenu dans une chaîne de caractères" signifie que le fichier ne contient pas de caractères de code 0, vous pouvez également utiliser la fonction getdelim(), qui accepte un bloc de mémoire et le réalloue si nécessaire, ou alloue simplement le tampon entier pour vous, et lit le fichier dans celui-ci jusqu'à ce qu'il rencontre un délimiteur spécifié ou la fin du fichier. Passez simplement ' \0 ' comme délimiteur pour lire le fichier entier.

Cette fonction est disponible dans la bibliothèque C de GNU, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994

L'exemple de code peut être aussi simple que

char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
  /* Success, now the entire file is in the buffer */

10voto

Jake Points 556

Si vous lisez des fichiers spéciaux comme stdin ou un pipe, vous ne pourrez pas utiliser fstat pour obtenir la taille du fichier au préalable. De plus, si vous lisez un fichier binaire, fgets perdra l'information sur la taille de la chaîne de caractères à cause de l'inclusion de ' \0 des personnages. La meilleure façon de lire un fichier est alors d'utiliser read and realloc :

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

int main () {
    char buf[4096];
    ssize_t n;
    char *str = NULL;
    size_t len = 0;
    while (n = read(STDIN_FILENO, buf, sizeof buf)) {
        if (n < 0) {
            if (errno == EAGAIN)
                continue;
            perror("read");
            break;
        }
        str = realloc(str, len + n + 1);
        memcpy(str + len, buf, n);
        len += n;
        str[len] = '\0';
    }
    printf("%.*s\n", len, str);
    return 0;
}

6voto

Joe Cool Points 135

Note : Il s'agit d'une modification de la réponse acceptée ci-dessus.

Voici un moyen de le faire, avec vérification des erreurs.

J'ai ajouté un vérificateur de taille pour quitter lorsque le fichier est plus grand que 1 GiB. Je l'ai fait parce que le programme met tout le fichier dans une chaîne de caractères, ce qui peut utiliser trop de RAM et faire planter l'ordinateur. Cependant, si vous ne vous souciez pas de cela, vous pouvez simplement le supprimer du code.

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

#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3

char * c_read_file(const char * f_name, int * err, size_t * f_size) {
    char * buffer;
    size_t length;
    FILE * f = fopen(f_name, "rb");
    size_t read_length;

    if (f) {
        fseek(f, 0, SEEK_END);
        length = ftell(f);
        fseek(f, 0, SEEK_SET);

        // 1 GiB; best not to load a whole large file in one string
        if (length > 1073741824) {
            *err = FILE_TOO_LARGE;

            return NULL;
        }

        buffer = (char *)malloc(length + 1);

        if (length) {
            read_length = fread(buffer, 1, length, f);

            if (length != read_length) {
                 free(buffer);
                 *err = FILE_READ_ERROR;

                 return NULL;
            }
        }

        fclose(f);

        *err = FILE_OK;
        buffer[length] = '\0';
        *f_size = length;
    }
    else {
        *err = FILE_NOT_EXIST;

        return NULL;
    }

    return buffer;
}

Et pour vérifier les erreurs :

int err;
size_t f_size;
char * f_data;

f_data = c_read_file("test.txt", &err, &f_size);

if (err) {
    // process error
}
else {
    // process data
    free(f_data);
}

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