web-dev-qa-db-fra.com

Comment lire le contenu d'un fichier en une chaîne en C?

Quel est le moyen le plus simple (le moins sujet aux erreurs, le moins de lignes de code, quelle que soit l'interprétation que vous vouliez en faire) pour ouvrir un fichier en C et lire son contenu dans une chaîne (char *, char [], peu importe)?

63
Chris Bunch

J'ai tendance à simplement charger tout le tampon sous forme de mémoire brute et à analyser moi-même. De cette façon, je contrôle mieux ce que la bibliothèque standard fait sur plusieurs plates-formes.

C'est un bout que j'utilise pour ça. vous pouvez aussi vouloir 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...
}
106
Nils Pipenbrinck

Une autre solution, malheureusement très dépendante du système d'exploitation, est le mappage en mémoire du fichier. Les avantages incluent généralement les performances de lecture et une utilisation réduite de la mémoire, car la vue des applications et le cache de fichiers des systèmes 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 en revanche est un peu plus compliqué, et malheureusement, je n'ai pas de compilateur devant moi pour tester, mais la fonctionnalité est fournie par CreateFileMapping() et MapViewOfFile().

22
Jeff Mc

Si "lit son contenu dans une chaîne" 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éaffecte si nécessaire, ou alloue simplement le tampon entier à vous, et lit le fichier dans celui-ci jusqu’à ce qu’il rencontre un délimiteur spécifié ou une fin de fichier. Il suffit de passer '\ 0' comme délimiteur pour lire le fichier entier.

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

L'exemple de code peut sembler 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 */
8
dmityugov

Si vous lisez des fichiers spéciaux tels que stdin ou un tube, vous ne pourrez pas utiliser fstat pour obtenir la taille de fichier au préalable. De plus, si vous lisez un fichier binaire, fgets va perdre les informations de taille de chaîne à cause des caractères "\ 0" incorporés. Le meilleur moyen de lire un fichier est d’utiliser read et 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;
}
5
Jake

Si le fichier est du texte et que vous voulez obtenir le texte ligne par ligne, le moyen le plus simple consiste à utiliser fgets ().

char buffer[100];
FILE *fp = fopen("filename", "r");                 // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
5
selwyn

Si vous utilisez glib, vous pouvez utiliser g_file_get_contents ;

gchar *contents;
GError *err = NULL;

g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
  {
    // Report error to user, and free error
    g_assert (contents == NULL);
    fprintf (stderr, "Unable to read file: %s\n", err->message);
    g_error_free (err);
  }
else
  {
    // Use file contents
    g_assert (contents != NULL);
  }
}
1
sleepycal

Juste modifié de la réponse acceptée ci-dessus.

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

char *readFile(char *filename) {
    FILE *f = fopen(filename, "rt");
    assert(f);
    fseek(f, 0, SEEK_END);
    long length = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buffer = (char *) malloc(length + 1);
    buffer[length] = '\0';
    fread(buffer, 1, length, f);
    fclose(f);
    return buffer;
}

int main() {
    char *content = readFile("../hello.txt");
    printf("%s", content);
}
0
BaiJiFeiLong
// Assumes the file exists and will seg. fault otherwise.
const GLchar *load_shader_source(char *filename) {
  FILE *file = fopen(filename, "r");             // open 
  fseek(file, 0L, SEEK_END);                     // find the end
  size_t size = ftell(file);                     // get the size in bytes
  GLchar *shaderSource = calloc(1, size);        // allocate enough bytes
  rewind(file);                                  // go back to file beginning
  fread(shaderSource, size, sizeof(char), file); // read each char into ourblock
  fclose(file);                                  // close the stream
  return shaderSource;
}

C'est une solution assez grossière car rien n'est vérifié avec null. 

0
Entalpi

Je vais ajouter ma propre version, basée sur les réponses ici, juste pour référence. Mon code prend en compte sizeof (char) et ajoute quelques commentaires.

// Open the file in read mode.
FILE *file = fopen(file_name, "r");
// Check if there was an error.
if (file == NULL) {
    fprintf(stderr, "Error: Can't open file '%s'.", file_name);
    exit(EXIT_FAILURE);
}
// Get the file length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Create the string for the file contents.
char *buffer = malloc(sizeof(char) * (length + 1));
buffer[length] = '\0';
// Set the contents of the string.
fread(buffer, sizeof(char), length, file);
// Close the file.
fclose(file);
// Do something with the data.
// ...
// Free the allocated string space.
free(buffer);
0
Erik Campobadal

Note: Ceci est 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 à quitter lorsque le fichier était supérieur à 1 Go. Je l’ai fait parce que le programme place le fichier entier dans une chaîne qui risque d’utiliser trop de RAM et de planter un ordinateur. Cependant, si cela vous est égal, vous pouvez simplement le supprimer du code.

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

#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TO_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 hole large file in one string
        if (length > 1073741824) {
            *err = FILE_TO_LARGE;

            return NULL;
        }

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

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

            if (length != read_length) {
                 *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
}
0
Joe Cool

facile et soigné (en supposant que le contenu du fichier soit inférieur à 10 000):

void read_whole_file(char fileName[1000], char buffer[10000])
{
    FILE * file = fopen(fileName, "r");
    if(file == NULL)
    {
        puts("File not found");
        exit(1);
    }
    char  c;
    int idx=0;
    while (fscanf(file , "%c" ,&c) == 1)
    {
        buffer[idx] = c;
        idx++;
    }
    buffer[idx] = 0;
}
0