En C, comment lire un fichier texte et imprimer toutes les chaînes

94

J'ai un fichier texte nommé test.txt

Je veux écrire un programme C qui peut lire ce fichier et imprimer le contenu sur la console (supposons que le fichier ne contienne que du texte ASCII).

Je ne sais pas comment obtenir la taille de ma variable de chaîne. Comme ça:

char str[999];
FILE * file;
file = fopen( "test.txt" , "r");
if (file) {
    while (fscanf(file, "%s", str)!=EOF)
        printf("%s",str);
    fclose(file);
}

La taille 999ne fonctionne pas car la chaîne renvoyée par fscanfpeut être plus grande que cela. Comment puis-je résoudre ça?

Richard
la source

Réponses:

134

Le moyen le plus simple est de lire un caractère et de l'imprimer juste après la lecture:

int c;
FILE *file;
file = fopen("test.txt", "r");
if (file) {
    while ((c = getc(file)) != EOF)
        putchar(c);
    fclose(file);
}

cest intau - dessus, car EOFest un nombre négatif, et une plaine charpeut l'être unsigned.

Si vous souhaitez lire le fichier par blocs, mais sans allocation de mémoire dynamique, vous pouvez faire:

#define CHUNK 1024 /* read 1024 bytes at a time */
char buf[CHUNK];
FILE *file;
size_t nread;

file = fopen("test.txt", "r");
if (file) {
    while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
        fwrite(buf, 1, nread, stdout);
    if (ferror(file)) {
        /* deal with error */
    }
    fclose(file);
}

La deuxième méthode ci-dessus est essentiellement la façon dont vous lirez un fichier avec un tableau alloué dynamiquement:

char *buf = malloc(chunk);

if (buf == NULL) {
    /* deal with malloc() failure */
}

/* otherwise do this.  Note 'chunk' instead of 'sizeof buf' */
while ((nread = fread(buf, 1, chunk, file)) > 0) {
    /* as above */
}

Votre méthode fscanf()avec %sas format perd des informations sur les espaces dans le fichier, donc elle ne copie pas exactement un fichier dans stdout.

Alok Singhal
la source
Il est possible de lire les données d'un fichier sans ouvrir ce fichier en c / c ++ ??
Sagar Patel
que faire si le fichier texte contient des valeurs entières séparées par des virgules? que ce que serait le code, pouvez-vous modifier votre réponse avec cela aussi.
Mohsin
Ce qui précède fonctionne pour tout type de fichier texte. Si vous souhaitez analyser les nombres à partir d'un fichier CSV, c'est un problème différent.
Alok Singhal
1
@overexchange La question ne parle pas de lignes - il s'agit de lire un fichier et de copier son contenu stdout.
Alok Singhal
1
@shjeff Un fichier ne peut pas contenir de caractère EOF. Notez que cc'est int, et C garantira qu'il EOFn'est égal à aucun caractère valide.
Alok Singhal
60

Il y a beaucoup de bonnes réponses ici sur la lecture en morceaux, je vais juste vous montrer une petite astuce qui lit tout le contenu à la fois dans un tampon et l'imprime.

Je ne dis pas que c'est mieux. Ce n'est pas le cas, et comme Ricardo parfois ça peut être mauvais, mais je trouve que c'est une bonne solution pour les cas simples.

Je l'ai parsemé de commentaires car il se passe beaucoup de choses.

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

char* ReadFile(char *filename)
{
   char *buffer = NULL;
   int string_size, read_size;
   FILE *handler = fopen(filename, "r");

   if (handler)
   {
       // Seek the last byte of the file
       fseek(handler, 0, SEEK_END);
       // Offset from the first to the last byte, or in other words, filesize
       string_size = ftell(handler);
       // go back to the start of the file
       rewind(handler);

       // Allocate a string that can hold it all
       buffer = (char*) malloc(sizeof(char) * (string_size + 1) );

       // Read it all in one operation
       read_size = fread(buffer, sizeof(char), string_size, handler);

       // fread doesn't set it so put a \0 in the last position
       // and buffer is now officially a string
       buffer[string_size] = '\0';

       if (string_size != read_size)
       {
           // Something went wrong, throw away the memory and set
           // the buffer to NULL
           free(buffer);
           buffer = NULL;
       }

       // Always remember to close the file.
       fclose(handler);
    }

    return buffer;
}

int main()
{
    char *string = ReadFile("yourfile.txt");
    if (string)
    {
        puts(string);
        free(string);
    }

    return 0;
}

Faites-moi savoir si c'est utile ou si vous pourriez en apprendre quelque chose :)

lfzawacki
la source
2
Ne devrait-il pas lire buffer[string_size] = '\0';au lieu de string_size+1? Afaik la chaîne réelle va de 0à string_size-1et le \0caractère doit donc être à string_size, non?
aepsil0n
4
Utiliser ftellet fseekpour trouver la taille d'un fichier n'est pas sûr: securecoding.cert.org/confluence/display/seccode/…
Joakim
1
Ce code contient une fuite de mémoire, vous ne fermez jamais le fichier. Il manque unfclose(handle)
Joakim
1
Il y a une faute de frappe où vous appelez fclose (handle), cela devrait être fclose (handler)
Eduardo Cobuci
3
Vous pouvez utiliser calloc(2)plutôt que de ne malloc(1)pas avoir à définir le terminateur nul.
14

Au lieu de cela, imprimez directement les caractères sur la console car le fichier texte peut être très volumineux et vous pouvez avoir besoin de beaucoup de mémoire.

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

int main() {

    FILE *f;
    char c;
    f=fopen("test.txt","rt");

    while((c=fgetc(f))!=EOF){
        printf("%c",c);
    }

    fclose(f);
    return 0;
}
Sagar Shah
la source
6

Utilisez "read ()" à la place de fscanf:

ssize_t read(int fildes, void *buf, size_t nbyte);

LA DESCRIPTION

La fonction read () tentera de lire les nbyteoctets du fichier associé au descripteur de fichier ouvert,, fildesdans le tampon pointé par buf.

Voici un exemple:

http://cmagical.blogspot.com/2010/01/c-programming-on-unix-implementing-cat.html

Partie de travail de cet exemple:

f=open(argv[1],O_RDONLY);
while ((n=read(f,l,80)) > 0)
    write(1,l,n);

Une autre approche consiste à utiliser getc/ putcpour lire / écrire 1 caractère à la fois. Beaucoup moins efficace. Un bon exemple: http://www.eskimo.com/~scs/cclass/notes/sx13.html

DVK
la source
readvous permettra de lire un certain nombre de caractères. Lisez suffisamment pour remplir votre tampon, puis videz votre tampon sur l'écran, effacez-le et répétez jusqu'à ce que vous arriviez à la fin du fichier.
bta
1

Deux approches me viennent à l'esprit.

Tout d'abord, ne l'utilisez pas scanf. Utilisez fgets()qui prend un paramètre pour spécifier la taille de la mémoire tampon, et qui laisse les caractères de nouvelle ligne intacts. Une simple boucle sur le fichier qui imprime le contenu du tampon devrait naturellement copier le fichier intact.

Deuxièmement, utilisez fread()ou l'idiome C commun avec fgetc(). Ceux-ci traiteraient le fichier en morceaux de taille fixe ou un seul caractère à la fois.

Si vous devez traiter le fichier sur des chaînes délimitées par des espaces, utilisez l'un fgetsou l' autre freadpour lire le fichier, et quelque chose comme strtokpour diviser le tampon en espace blanc. N'oubliez pas de gérer la transition d'un tampon à l'autre, car vos chaînes cibles sont susceptibles de couvrir la limite du tampon.

S'il existe une exigence externe à utiliser scanfpour effectuer la lecture, limitez la longueur de la chaîne qu'il peut lire avec un champ de précision dans le spécificateur de format. Dans votre cas avec un tampon de 999 octets, dites scanf("%998s", str);qui écrira au plus 998 caractères dans le tampon en laissant de la place pour le terminateur nul. Si des chaînes simples plus longues que votre tampon sont autorisées, vous devrez les traiter en deux parties. Sinon, vous avez la possibilité d'informer poliment l'utilisateur d'une erreur sans créer de trou de sécurité de dépassement de la mémoire tampon.

Quoi qu'il en soit, validez toujours les valeurs de retour et réfléchissez à la manière de gérer les entrées incorrectes, malveillantes ou simplement mal formées.

RBerteig
la source
1

Vous pouvez utiliser fgetset limiter la taille de la chaîne de lecture.

char *fgets(char *str, int num, FILE *stream);

Vous pouvez modifier le whiledans votre code en:

while (fgets(str, 100, file)) /* printf("%s", str) */;
Edu
la source
0

Vous pouvez lire le fichier entier avec une allocation de mémoire dynamique, mais ce n'est pas une bonne idée car si le fichier est trop gros, vous pourriez avoir des problèmes de mémoire.

Il vaut donc mieux lire de courtes parties du fichier et l'imprimer.

#include <stdio.h>
#define BLOCK   1000

int main() {
    FILE *f=fopen("teste.txt","r");
    int size;
    char buffer[BLOCK];
    // ...
    while((size=fread(buffer,BLOCK,sizeof(char),f)>0)
            fwrite(buffer,size,sizeof(char),stdout);
    fclose(f);
    // ...
    return 0;
}
Rigon
la source