2010-01-08 36 views
21

Tôi đang làm việc với các file văn bản nhỏ mà tôi muốn đọc vào một bộ đệm trong khi tôi xử lý chúng, vì vậy tôi đã đi lên với đoạn mã sau:Cách đúng để đọc một tệp văn bản vào bộ đệm trong C?

... 
char source[1000000]; 

FILE *fp = fopen("TheFile.txt", "r"); 
if(fp != NULL) 
{ 
    while((symbol = getc(fp)) != EOF) 
    { 
     strcat(source, &symbol); 
    } 
    fclose(fp); 
} 
... 

Đây có phải là cách chính xác đưa nội dung của tệp vào bộ đệm hoặc tôi đang lạm dụng strcat()?

sau đó tôi lặp thông qua bộ đệm như sau:

for(int x = 0; (c = source[x]) != '\0'; x++) 
{ 
    //Process chars 
} 
+1

Điều này là sai. 'strcat' nối chuỗi. Ngay cả khi '& symbol' là một' char * ', nó không phải là null-terminated. Bạn nên sử dụng 'fgets' hoặc' fread'. Ngoài ra, 'strcat' sẽ chậm trong trường hợp của bạn vì nó quét' nguồn' mỗi khi cần thêm một ký tự. –

+14

Có, bạn đang lạm dụng strcat. Làm ơn. Dừng lại đi. Lạm dụng mèo là sai. –

+0

Không đề cập đến việc đọc một char tại một thời điểm sẽ chậm hơn nhiều so với sử dụng 'fread'. –

Trả lời

62
char source[1000000]; 

FILE *fp = fopen("TheFile.txt", "r"); 
if(fp != NULL) 
{ 
    while((symbol = getc(fp)) != EOF) 
    { 
     strcat(source, &symbol); 
    } 
    fclose(fp); 
} 

Có khá một vài điều sai với mã này:

  1. Nó rất chậm (bạn đang chiết xuất nhân vật đệm cùng một lúc).
  2. Nếu kích thước tệp vượt quá sizeof(source), điều này dễ bị tràn bộ đệm.
  3. Thực sự, khi bạn xem xét kỹ hơn, mã này sẽ không hoạt động. Như đã trình bày trong các trang người đàn ông:

Chức năng strcat() gắn thêm một bản sao của chuỗi null-chấm dứt s2 đến cuối của chuỗi s1 null-chấm dứt, sau đó thêm một chấm dứt `\ 0' .

Bạn đang bổ sung một nhân vật (không phải là một chuỗi NUL-chấm dứt!) Thành một chuỗi có thể có hoặc không có thể NUL-chấm dứt. Các chỉ thời gian tôi có thể tưởng tượng này làm việc theo mô tả man-page là nếu mỗi nhân vật trong tập tin là NUL chấm dứt, trong trường hợp này sẽ là khá vô nghĩa. Vì vậy, có, đây chắc chắn là một sự lạm dụng khủng khiếp của strcat().

Sau đây là hai lựa chọn thay thế để xem xét sử dụng thay thế.

Nếu bạn biết kích thước bộ đệm tối đa trước thời hạn:

#include <stdio.h> 
#define MAXBUFLEN 1000000 

char source[MAXBUFLEN + 1]; 
FILE *fp = fopen("foo.txt", "r"); 
if (fp != NULL) { 
    size_t newLen = fread(source, sizeof(char), MAXBUFLEN, fp); 
    if (ferror(fp) != 0) { 
     fputs("Error reading file", stderr); 
    } else { 
     source[newLen++] = '\0'; /* Just to be safe. */ 
    } 

    fclose(fp); 
} 

Hoặc, nếu bạn không:

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

char *source = NULL; 
FILE *fp = fopen("foo.txt", "r"); 
if (fp != NULL) { 
    /* Go to the end of the file. */ 
    if (fseek(fp, 0L, SEEK_END) == 0) { 
     /* Get the size of the file. */ 
     long bufsize = ftell(fp); 
     if (bufsize == -1) { /* Error */ } 

     /* Allocate our buffer to that size. */ 
     source = malloc(sizeof(char) * (bufsize + 1)); 

     /* Go back to the start of the file. */ 
     if (fseek(fp, 0L, SEEK_SET) != 0) { /* Error */ } 

     /* Read the entire file into memory. */ 
     size_t newLen = fread(source, sizeof(char), bufsize, fp); 
     if (ferror(fp) != 0) { 
      fputs("Error reading file", stderr); 
     } else { 
      source[newLen++] = '\0'; /* Just to be safe. */ 
     } 
    } 
    fclose(fp); 
} 

free(source); /* Don't forget to call free() later! */ 
+2

Có thể bạn cũng muốn vô hiệu hóa bộ đệm của mình. Trong mẫu mã thứ hai của bạn, bạn đã rời khỏi chỗ trống, nhưng không thực sự thiết lập nó; trong lần đầu tiên của bạn, bạn đã bỏ quên để lại chỗ trống. –

+0

@Brian: Đúng vậy, tôi đã cập nhật các ví dụ với ý nghĩ đó. – Michael

+0

+1 cho việc sử dụng ftell và malloc. Đây là con đường để đi. – cigarman

4

Có - bạn sẽ có thể bị bắt vì lạm dụng terriable lại strcat! Hãy xem getline() nó đọc dữ liệu một dòng tại một thời điểm nhưng quan trọng nó có thể giới hạn số lượng ký tự bạn đọc, vì vậy bạn không tràn bộ đệm.

Strcat tương đối chậm vì nó phải tìm kiếm toàn bộ chuỗi để kết thúc khi chèn từng ký tự. Thông thường bạn sẽ giữ một con trỏ đến đầu lưu trữ chuỗi hiện tại và chuyển nó tới vị trí để đọc dòng tiếp theo.

+0

Giải pháp thay thế là gì? –

1

Xem this article from JoelOnSoftware vì lý do bạn không muốn sử dụng strcat.

Nhìn vào fread để thay thế. Sử dụng nó với 1 cho kích thước khi bạn đang đọc byte hoặc ký tự.

+0

Bài viết hay ... –

1

Tại sao bạn không chỉ cần sử dụng các mảng ký tự bạn có? Điều này nên để làm điều đó:

source[i] = getc(fp); 
    i++; 
1

Không thử nghiệm, nhưng nên làm việc .. Và vâng, nó có thể là thực hiện tốt hơn với fread, tôi sẽ để nó như một bài tập cho người đọc.

#define DEFAULT_SIZE 100 
#define STEP_SIZE 100 

char *buffer[DEFAULT_SIZE]; 
size_t buffer_sz=DEFAULT_SIZE; 
size_t i=0; 
while(!feof(fp)){ 
    buffer[i]=fgetc(fp); 
    i++; 
    if(i>=buffer_sz){ 
    buffer_sz+=STEP_SIZE; 
    void *tmp=buffer; 
    buffer=realloc(buffer,buffer_sz); 
    if(buffer==null){ free(tmp); exit(1);} //ensure we don't have a memory leak 
    } 
} 
buffer[i]=0; 
+0

sẽ không 'realloc' bị chậm? – ajay

+0

Sorta, nhưng bạn thực sự cần phải lo lắng về 'char * buffer [DEFAULT_SIZE]' bởi vì nó là một mảng con trỏ, không phải của các ký tự. Việc gán cho 'buffer [i]' là đáng ngờ nhất; 'fgetc()' trả về 'char', không phải' char * '. Nếu chúng ta giả vờ rằng nó là 'char * buffer = 0;', bạn gần như ở đó. Bạn cần phải đọc các ký tự vào một 'int', và chỉ stash nó trong mảng khi bạn chắc chắn nó không phải là EOF và có đủ không gian. ['while (! feof (file))' luôn luôn sai] (http://stackoverflow.com/questions/5431941/)! Câu trả lời này cần công việc đáng kể (nhưng là cơ sở cho những gì có thể là một câu trả lời hay). –

0

Nếu bạn đang ở trên một hệ thống Linux, một khi bạn có mô tả tập tin bạn có thể nhận được rất nhiều thông tin về các tập tin sử dụng fstat()

http://linux.die.net/man/2/stat

để bạn có thể có

#include <unistd.h> 
void main() 
{ 
    struct stat stat; 
    int fd; 
    //get file descriptor 
    fstat(fd, &stat); 
    //the size of the file is now in stat.st_size 
} 

Điều này tránh tìm cách bắt đầu và kết thúc tệp.

Các vấn đề liên quan