ในภาษา C ฉันจะอ่านไฟล์ข้อความและพิมพ์สตริงทั้งหมดได้อย่างไร


94

ฉันมีไฟล์ข้อความชื่อ test.txt

ฉันต้องการเขียนโปรแกรม C ที่สามารถอ่านไฟล์นี้และพิมพ์เนื้อหาไปยังคอนโซล (สมมติว่าไฟล์มีเฉพาะข้อความ ASCII)

ฉันไม่ทราบวิธีรับขนาดของตัวแปรสตริงของฉัน แบบนี้:

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

ขนาด999ไม่ทำงานเนื่องจากสตริงที่ส่งคืนfscanfอาจมีขนาดใหญ่กว่านั้น ฉันจะแก้ปัญหานี้ได้อย่างไร?

คำตอบ:


134

วิธีที่ง่ายที่สุดคืออ่านอักขระและพิมพ์ทันทีหลังจากอ่าน:

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

cคือintข้างต้นตั้งแต่EOFเป็นจำนวนลบและธรรมดาอาจจะcharunsigned

หากคุณต้องการอ่านไฟล์เป็นชิ้น ๆ แต่ไม่มีการจัดสรรหน่วยความจำแบบไดนามิกคุณสามารถทำได้:

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

วิธีที่สองข้างต้นเป็นวิธีที่คุณจะอ่านไฟล์ด้วยอาร์เรย์ที่จัดสรรแบบไดนามิก:

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

วิธีการของคุณfscanf()กับเป็นรูปแบบการสูญเสียข้อมูลเกี่ยวกับช่องว่างในแฟ้มจึงไม่ได้ว่าการคัดลอกไฟล์ไปยัง%sstdout


เป็นไปได้ที่จะอ่านข้อมูลจากไฟล์โดยไม่ต้องเปิดไฟล์นั้นใน c / c ++ ??
Sagar Patel

จะเกิดอะไรขึ้นถ้าไฟล์ข้อความมีค่าจำนวนเต็มที่คั่นด้วยเครื่องหมายจุลภาค คุณสามารถแก้ไขคำตอบของคุณได้มากกว่าโค้ดที่อยู่ในนั้นด้วย
Mohsin

ข้างต้นใช้ได้กับไฟล์ข้อความทุกประเภท หากคุณต้องการแยกวิเคราะห์ตัวเลขจากไฟล์ CSV นั่นเป็นปัญหาอื่น
Alok Singhal

1
@overexchange คำถามไม่ได้พูดคุยเกี่ยวกับเส้น - stdoutมันเป็นเรื่องเกี่ยวกับการอ่านไฟล์และคัดลอกเนื้อหาไป
Alok Singhal

1
@shjeff ไฟล์ต้องไม่มีอักขระ EOF โปรดทราบว่าcเป็น int และ C จะรับประกันว่าEOFจะไม่เท่ากับอักขระที่ถูกต้อง
Alok Singhal

60

มีคำตอบที่ดีมากมายเกี่ยวกับการอ่านเป็นกลุ่มฉันจะแสดงเคล็ดลับเล็ก ๆ น้อย ๆ ที่อ่านเนื้อหาทั้งหมดพร้อมกันไปยังบัฟเฟอร์และพิมพ์ออกมา

ฉันไม่ได้บอกว่ามันดีกว่า ไม่ใช่และในบางครั้งริคาร์โดก็อาจไม่ดี แต่ฉันพบว่ามันเป็นทางออกที่ดีสำหรับกรณีง่ายๆ

ฉันโรยด้วยความคิดเห็นเพราะมีหลายอย่างเกิดขึ้น

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

แจ้งให้เราทราบหากมีประโยชน์หรือคุณสามารถเรียนรู้บางสิ่งจากมัน :)


2
มันไม่ควรอ่านbuffer[string_size] = '\0';แทนstring_size+1? Afaik สตริงจริงเปลี่ยนจาก0ถึงstring_size-1และ\0อักขระจึงต้องอยู่ที่string_sizeใช่ไหม?
aepsil0n

4
การใช้ftellและfseekเพื่อค้นหาขนาดของไฟล์นั้นไม่ปลอดภัย: securecoding.cert.org/confluence/display/seccode/…
Joakim

1
รหัสนี้มีหน่วยความจำรั่วคุณจะไม่ปิดไฟล์ มีผู้สูญหายfclose(handle)
Joakim

1
มีการพิมพ์ผิดที่คุณเรียกว่า fclose (หมายเลขอ้างอิง) ควรเป็น fclose (ตัวจัดการ)
Eduardo Cobuci

3
คุณสามารถใช้calloc(2)แทนที่จะmalloc(1)ข้ามไปโดยไม่ต้องตั้งค่า null terminator

14

แต่เพียงแค่พิมพ์อักขระลงบนคอนโซลโดยตรงเนื่องจากไฟล์ข้อความอาจมีขนาดใหญ่มากและคุณอาจต้องใช้หน่วยความจำจำนวนมาก

#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

ใช้ "read ()" แทน o fscanf:

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

คำอธิบาย

อ่าน () ฟังก์ชันจะพยายามที่จะอ่านnbyteไบต์จากไฟล์ที่เกี่ยวข้องกับการอธิบายไฟล์เปิดเข้าไปในบัฟเฟอร์ที่ชี้ไปตามfildesbuf

นี่คือตัวอย่าง:

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

ส่วนการทำงานจากตัวอย่างนั้น:

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

วิธีอื่นคือใช้getc/ putcอ่าน / เขียนครั้งละ 1 อักขระ มีประสิทธิภาพน้อยกว่ามาก ตัวอย่างที่ดี: http://www.eskimo.com/~scs/cclass/notes/sx13.html


readจะช่วยให้คุณอ่านเป็นจำนวนอักขระที่กำหนด อ่านให้เพียงพอเพื่อเติมบัฟเฟอร์ของคุณจากนั้นถ่ายโอนข้อมูลบัฟเฟอร์ของคุณไปที่หน้าจอล้างออกและทำซ้ำจนกว่าคุณจะไปถึงจุดสิ้นสุดของไฟล์
bta

1

สองแนวทางที่จะนึกถึง

scanfครั้งแรกที่ไม่ได้ใช้ ใช้fgets()ซึ่งรับพารามิเตอร์เพื่อระบุขนาดบัฟเฟอร์และซึ่งทำให้อักขระขึ้นบรรทัดใหม่ไม่เสียหาย การวนซ้ำอย่างง่ายบนไฟล์ที่พิมพ์เนื้อหาบัฟเฟอร์ควรคัดลอกไฟล์ตามธรรมชาติ

ประการที่สองใช้fread()หรือสำนวน C ทั่วไปกับfgetc(). สิ่งเหล่านี้จะประมวลผลไฟล์เป็นชิ้นขนาดคงที่หรือทีละอักขระ

หากคุณต้องประมวลผลไฟล์บนสตริงที่คั่นด้วยเว้นวรรคสีขาวให้ใช้อย่างใดอย่างหนึ่งfgetsหรือfreadเพื่ออ่านไฟล์และสิ่งที่ต้องการstrtokแยกบัฟเฟอร์ที่ช่องว่าง อย่าลืมจัดการการเปลี่ยนจากบัฟเฟอร์หนึ่งไปยังอีกอันหนึ่งเนื่องจากสตริงเป้าหมายของคุณมีแนวโน้มที่จะขยายขอบเขตบัฟเฟอร์

หากมีข้อกำหนดภายนอกเพื่อใช้scanfในการอ่านให้จำกัดความยาวของสตริงที่อาจอ่านด้วยฟิลด์ความแม่นยำในตัวระบุรูปแบบ ในกรณีของคุณที่มีบัฟเฟอร์ 999 ไบต์ให้ระบุว่าตัวscanf("%998s", str);ใดจะเขียนอักขระได้มากที่สุด 998 ตัวในช่องเว้นบัฟเฟอร์สำหรับเทอร์มิเนเตอร์ nul หากสตริงเดี่ยวยาวเกินกว่าที่อนุญาตให้ใช้บัฟเฟอร์คุณจะต้องประมวลผลเป็นสองส่วน หากไม่เป็นเช่นนั้นคุณมีโอกาสที่จะแจ้งผู้ใช้เกี่ยวกับข้อผิดพลาดอย่างสุภาพโดยไม่ต้องสร้างช่องโหว่ด้านความปลอดภัยของบัฟเฟอร์ล้น

ไม่ว่าจะตรวจสอบความถูกต้องของค่าที่ส่งคืนเสมอและคิดเกี่ยวกับวิธีจัดการอินพุตที่ไม่ถูกต้องเป็นอันตรายหรือมีรูปแบบที่ผิดปกติ


1

คุณสามารถใช้fgetsและ จำกัด ขนาดของสตริงการอ่านได้

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

คุณสามารถเปลี่ยนwhileรหัสของคุณเป็น:

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

0

คุณสามารถอ่านไฟล์ทั้งหมดด้วยการจัดสรรหน่วยความจำแบบไดนามิก แต่ไม่ใช่ความคิดที่ดีเพราะถ้าไฟล์ใหญ่เกินไปคุณอาจมีปัญหากับหน่วยความจำ

ดังนั้นควรอ่านส่วนสั้น ๆ ของไฟล์และพิมพ์

#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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.