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.) ?
Réponses
Trop de publicités?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...
}
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()
.
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 */
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;
}
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);
}
- Réponses précédentes
- Plus de réponses