In C, come devo leggere un file di testo e stampare tutte le stringhe


94

Ho un file di testo denominato test.txt

Voglio scrivere un programma C in grado di leggere questo file e stampare il contenuto sulla console (supponiamo che il file contenga solo testo ASCII).

Non so come ottenere la dimensione della mia variabile stringa. Come questo:

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

La dimensione 999non funziona perché la stringa restituita da fscanfpuò essere maggiore di quella. Come posso risolvere questo?

Risposte:


134

Il modo più semplice è leggere un carattere e stamparlo subito dopo averlo letto:

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

cè al di intsopra, poiché EOFè un numero negativo e charpuò essere un semplice unsigned.

Se vuoi leggere il file in blocchi, ma senza l'allocazione dinamica della memoria, puoi fare:

#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);
}

Il secondo metodo sopra è essenzialmente come leggere un file con un array allocato dinamicamente:

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 */
}

Il tuo metodo di formato fscanf()con %sas perde le informazioni sugli spazi nel file, quindi non sta esattamente copiando un file in stdout.


È possibile leggere i dati dal file senza aprire quel file in c / c ++ ??
Sagar Patel

cosa succede se il file di testo contiene valori interi separati da virgole? di quello che sarebbe il codice puoi modificare la tua risposta anche con quello.
Mohsin

Quanto sopra funziona per qualsiasi tipo di file di testo. Se vuoi analizzare i numeri da un file CSV, questo è un problema diverso.
Alok Singhal

1
@overexchange La domanda non parla di righe: si tratta di leggere un file e copiarne il contenuto stdout.
Alok Singhal

1
@shjeff Un file non può contenere caratteri EOF. Nota che cè int e C garantirà che EOFnon è uguale a nessun carattere valido.
Alok Singhal

60

Ci sono molte buone risposte qui sulla lettura in blocchi, ti mostrerò solo un piccolo trucco che legge tutto il contenuto in una volta su un buffer e lo stampa.

Non sto dicendo che sia meglio. Non lo è, e come Ricardo a volte può essere un male, ma trovo che sia una bella soluzione per i casi semplici.

L'ho cosparso di commenti perché c'è molto da fare.

#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;
}

Fammi sapere se è utile o potresti imparare qualcosa da esso :)


2
Non dovrebbe leggere buffer[string_size] = '\0';invece di string_size+1? Afaik la stringa effettiva va da 0a string_size-1e quindi il \0carattere deve essere string_size, giusto?
aepsil0n

4
L'uso di ftelle fseekper trovare la dimensione di un file non è sicuro: securecoding.cert.org/confluence/display/seccode/…
Joakim

1
Questo codice contiene una perdita di memoria, non si chiude mai il file. C'è un dispersofclose(handle)
Joakim

1
C'è un errore di battitura in cui chiami fclose (handle), dovrebbe essere fclose (gestore)
Eduardo Cobuci,

3
Potresti usare calloc(2)piuttosto che malloc(1)saltare dover impostare il terminatore nullo.

14

Invece, stampa direttamente i caratteri sulla console perché il file di testo potrebbe essere molto grande e potresti richiedere molta memoria.

#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;
}

6

Usa "read ()" invece di fscanf:

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

DESCRIZIONE

La funzione read () tenterà di leggere i nbytebyte dal file associato al descrittore di file aperto,, fildesnel buffer a cui punta buf.

Ecco un esempio:

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

Parte funzionante da quell'esempio:

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

Un approccio alternativo consiste nell'usare getc/ putcleggere / scrivere 1 carattere alla volta. Molto meno efficiente. Un buon esempio: http://www.eskimo.com/~scs/cclass/notes/sx13.html


readti permetterà di leggere un certo numero di caratteri. Leggi abbastanza da riempire il tuo buffer, quindi scarica il buffer sullo schermo, cancellalo e ripeti fino ad arrivare alla fine del file.
bta

1

Vengono in mente due approcci.

Primo, non usare scanf. Usa fgets()che accetta un parametro per specificare la dimensione del buffer e che lascia intatti i caratteri di nuova riga. Un semplice ciclo sul file che stampa il contenuto del buffer dovrebbe naturalmente copiare il file intatto.

Secondo, usa fread()o l'idioma C comune con fgetc(). Questi elaborerebbero il file in blocchi di dimensioni fisse o un singolo carattere alla volta.

Se è necessario elaborare il file su stringhe delimitate da spazi, utilizzare fgetso freadper leggere il file e qualcosa di simile strtoka dividere il buffer in spazi vuoti. Non dimenticare di gestire la transizione da un buffer al successivo, poiché è probabile che le stringhe di destinazione coprano il limite del buffer.

Se è presente un requisito esterno da utilizzare scanfper eseguire la lettura, limitare la lunghezza della stringa che potrebbe essere letta con un campo di precisione nell'identificatore di formato. Nel tuo caso con un buffer da 999 byte, allora dì scanf("%998s", str);che scriverà al massimo 998 caratteri nel buffer lasciando spazio al terminatore nul. Se sono consentite singole stringhe più lunghe del buffer, è necessario elaborarle in due parti. In caso contrario, hai l'opportunità di informare l'utente di un errore educatamente senza creare un buco di sicurezza di overflow del buffer.

Indipendentemente da ciò, convalida sempre i valori restituiti e pensa a come gestire input dannosi, dannosi o semplicemente non validi.


1

È possibile utilizzare fgetse limitare la dimensione della stringa letta.

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

Puoi cambiare il whilenel tuo codice in:

while (fgets(str, 100, file)) /* printf("%s", str) */;

0

Potresti leggere l'intero file con l'allocazione dinamica della memoria, ma non è una buona idea perché se il file è troppo grande, potresti avere problemi di memoria.

Quindi è meglio leggere brevi parti del file e stamparlo.

#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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.