Di C, bagaimana saya harus membaca file teks dan mencetak semua string

94

Saya memiliki file teks bernama test.txt

Saya ingin menulis program C yang dapat membaca file ini dan mencetak konten ke konsol (asumsikan file hanya berisi teks ASCII).

Saya tidak tahu bagaimana cara mendapatkan ukuran variabel string saya. Seperti ini:

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

Ukuran 999tidak berfungsi karena string yang dikembalikan oleh fscanfbisa lebih besar dari itu. Bagaimana saya bisa memecahkan masalah ini?

Richard
sumber

Jawaban:

134

Cara termudah adalah membaca karakter, dan mencetaknya segera setelah membaca:

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

cdi intatas, karena EOFadalah angka negatif, dan charmungkin polos unsigned.

Jika Anda ingin membaca file dalam potongan, tetapi tanpa alokasi memori dinamis, Anda dapat melakukan:

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

Metode kedua di atas pada dasarnya adalah bagaimana Anda akan membaca file dengan array yang dialokasikan secara dinamis:

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

Metode Anda fscanf()dengan %sformat sebagai kehilangan informasi tentang spasi di file, jadi tidak persis menyalin file ke stdout.

Alok Singhal
sumber
Dimungkinkan untuk membaca data dari file tanpa membuka file itu di c / c ++ ??
Sagar Patel
bagaimana jika file teks berisi nilai integer yang dipisahkan koma? daripada apa yang akan menjadi kode Anda dapat mengedit jawaban Anda dengan itu juga di dalamnya.
Mohsin
Cara di atas berfungsi untuk semua jenis file teks. Jika Anda ingin mengurai angka dari file CSV, itu masalah yang berbeda.
Alok Singhal
1
@ overexchange Pertanyaannya tidak berbicara tentang baris - ini tentang membaca file dan menyalin isinya ke stdout.
Alok Singhal
1
@shjeff File tidak boleh berisi karakter EOF. Perhatikan bahwa cint, dan C akan menjamin bahwa EOFtidak sama dengan karakter yang valid.
Alok Singhal
60

Ada banyak jawaban bagus di sini tentang membacanya dalam potongan, saya hanya akan menunjukkan kepada Anda sedikit trik yang membaca semua konten sekaligus ke buffer dan mencetaknya.

Saya tidak mengatakan itu lebih baik. Tidak, dan seperti Ricardo kadang-kadang bisa buruk, tapi menurut saya ini adalah solusi yang bagus untuk kasus-kasus sederhana.

Saya menaburkannya dengan komentar karena ada banyak hal yang terjadi.

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

Beri tahu saya jika itu berguna atau Anda dapat belajar sesuatu darinya :)

lfzawacki
sumber
2
Bukankah seharusnya membaca buffer[string_size] = '\0';bukan string_size+1? Afaik string sebenarnya berubah dari 0ke string_size-1dan \0karakter karenanya harus di string_size, kan?
aepsil0n
4
Menggunakan ftelldan fseekmenemukan ukuran file tidak aman: securecoding.cert.org/confluence/display/seccode/…
Joakim
1
Kode ini mengandung kebocoran memori, Anda tidak pernah menutup file. Ada yang hilangfclose(handle)
Joakim
1
Ada kesalahan ketik di mana Anda memanggil fclose (handle), itu harus fclose (handler)
Eduardo Cobuci
3
Anda dapat menggunakan calloc(2)daripada malloc(1)melewatkan harus mengatur terminator null.
14

Alih-alih langsung mencetak karakter ke konsol karena file teks mungkin sangat besar dan Anda mungkin memerlukan banyak memori.

#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
sumber
6

Gunakan "read ()" sebagai ganti o fscanf:

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

DESKRIPSI

Fungsi read () akan mencoba membaca nbytebyte dari file yang terkait dengan deskriptor file yang terbuka fildes, ke dalam buffer yang ditunjuk oleh buf.

Berikut ini contohnya:

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

Bagian yang dikerjakan dari contoh itu:

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

Pendekatan alternatif adalah dengan menggunakan getc/ putcmembaca / menulis 1 karakter sekaligus. Jauh kurang efisien. Contoh yang bagus: http://www.eskimo.com/~scs/cclass/notes/sx13.html

DVK
sumber
readakan memungkinkan Anda membaca dalam sejumlah karakter. Baca secukupnya untuk mengisi buffer Anda, lalu buang buffer Anda ke layar, kosongkan, dan ulangi hingga Anda mencapai akhir file.
bta
1

Dua pendekatan melompat ke pikiran.

Pertama, jangan gunakan scanf. Gunakan fgets()yang mengambil parameter untuk menentukan ukuran buffer, dan yang membiarkan karakter baris baru tetap utuh. Sebuah loop sederhana di atas file yang mencetak konten buffer secara alami harus menyalin file secara utuh.

Kedua, gunakan fread()atau idiom C umum dengan fgetc(). Ini akan memproses file dalam potongan berukuran tetap atau satu karakter dalam satu waktu.

Jika Anda harus memproses file melalui string yang dipisahkan spasi putih, gunakan salah satu fgetsatau freaduntuk membaca file, dan sesuatu seperti strtokmembagi buffer di spasi kosong. Jangan lupa untuk menangani transisi dari satu buffer ke buffer berikutnya, karena string target Anda kemungkinan besar menjangkau batas buffer.

Jika ada persyaratan eksternal yang dapat digunakan scanfuntuk melakukan pembacaan, maka batasi panjang string yang mungkin terbaca dengan bidang presisi dalam penentu format. Dalam kasus Anda dengan buffer 999 byte, lalu katakan scanf("%998s", str);mana yang akan menulis paling banyak 998 karakter ke buffer yang menyisakan ruang untuk terminator nul. Jika satu string lebih panjang dari buffer Anda yang diperbolehkan, maka Anda harus memprosesnya menjadi dua bagian. Jika tidak, Anda memiliki kesempatan untuk memberi tahu pengguna tentang kesalahan dengan sopan tanpa membuat lubang keamanan buffer overflow.

Terlepas dari itu, selalu validasi nilai yang dikembalikan dan pikirkan tentang cara menangani masukan yang buruk, berbahaya, atau hanya salah format.

RBerteig
sumber
1

Anda dapat menggunakan fgetsdan membatasi ukuran string baca.

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

Anda dapat mengubah whilekode Anda menjadi:

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

Anda dapat membaca seluruh file dengan alokasi memori dinamis, tetapi bukan ide yang baik karena jika file terlalu besar, Anda dapat mengalami masalah memori.

Jadi lebih baik membaca bagian pendek dari file dan mencetaknya.

#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
sumber