2008-11-03 34 views
8

Hãy xem xét các chương trình C đơn giản sau đây mà đọc một tập tin vào một bộ đệm và hiển thị mà bộ đệm ra cửa sổ Console:Tại sao chương trình C đơn giản của tôi lại hiển thị rác thải để stdout?

#include<stdio.h> 

main() 
{ 
    FILE *file; 
    char *buffer; 
    unsigned long fileLen; 
    //Open file 
    file = fopen("HelloWorld.txt", "rb"); 
    if (!file) 
    { 
     fprintf(stderr, "Unable to open file %s", "HelloWorld.txt"); 
     return; 
    } 
    //Get file length 
    fseek(file, 0, SEEK_END); 
    fileLen=ftell(file); 
    fseek(file, 0, SEEK_SET); 
    //Allocate memory 
    buffer=(char *)malloc(fileLen+1); 
    if (!buffer) 
    { 
     fprintf(stderr, "Memory error!"); 
     fclose(file); 
     return; 
    } 
    //Read file contents into buffer 
    fread(buffer, fileLen, 1, file); 
    //Send buffer contents to stdout 
    printf("%s\n",buffer);  
    fclose(file); 
} 

Các tập tin nó sẽ đọc chỉ đơn giản bao gồm:

Hello World!

Đầu ra là:

Hello World ²²²²▌▌▌▌▌▌▌↔☺

Nó đã được một thời gian kể từ khi tôi đã làm bất cứ điều gì có ý nghĩa trong C/C++, nhưng thông thường tôi sẽ giả định rằng bộ đệm đã được phân bổ lớn hơn mức cần thiết, nhưng điều này dường như không đúng.

tệpLen kết thúc bằng 12, chính xác.

Tôi đang nghĩ rằng bây giờ tôi phải chỉ hiển thị bộ đệm sai, nhưng tôi không chắc chắn những gì tôi đang làm sai.

Có ai có thể cho tôi biết điều tôi đang làm không?

Trả lời

38

Bạn cần phải NUL chấm dứt chuỗi của mình. Thêm

buffer[fileLen] = 0; 

trước khi in.

+1

Lấy -1 ra và bạn đã đúng! – GEOCHET

+0

Tôi đã cố gắng làm điều này tại một thời điểm, nhưng vì lý do nào đó có vẻ như tôi đã bị rạn não và sử dụng \ n thay vì \ 0 vì một lý do ngu ngốc nào đó. Nói với bạn tôi đã gỉ! – GEOCHET

+0

Tôi đã bỏ lỡ bộ đệm là fileLen + 1 byte dài ... – JesperE

8

JesperE là chính xác về vấn đề chấm dứt trong ví dụ của bạn, tôi sẽ chỉ thêm rằng nếu bạn đang xử lý tệp văn bản thì tốt hơn nên sử dụng fgets() hoặc một cái gì đó tương tự như điều này sẽ xử lý đúng trình tự dòng mới nền tảng và sẽ luôn luôn nul chấm dứt chuỗi cho bạn. Nếu bạn đang thực sự làm việc với dữ liệu nhị phân thì bạn không muốn sử dụng printf() để xuất dữ liệu như các hàm printf mong đợi các chuỗi và một byte nul trong dữ liệu sẽ gây ra việc cắt đầu ra.

+0

Cảm ơn lời khuyên. Đây là một tệp nhị phân (một phần của nó). Tôi chỉ sử dụng printf() tại thời điểm này để có được vòng bi của tôi và 'gỡ lỗi'. – GEOCHET

28

Cách tiếp cận của JesperE sẽ hoạt động, nhưng bạn có thể quan tâm để biết rằng có một cách khác để xử lý việc này.

Bạn luôn có thể in một chuỗi có độ dài được biết đến, ngay cả khi không có NUL-terminator, bằng cách cung cấp độ dài để printf như độ chính xác cho lĩnh vực chuỗi:

printf("%.*s\n", fileLen, buffer); 

này cho phép bạn in các chuỗi mà không sửa đổi bộ đệm.

0

Bạn có thể sử dụng calloc thay vì malloc để cấp phát bộ nhớ đã được khởi tạo. calloc nhận thêm đối số. Nó rất hữu ích cho việc phân bổ mảng; tham số đầu tiên của calloc cho biết số phần tử trong mảng mà bạn muốn cấp phát bộ nhớ cho, và đối số thứ hai là kích thước của mỗi phần tử. Kể từ khi kích thước của một char luôn là 1, chúng tôi chỉ có thể vượt qua 1 như là đối số thứ hai:

buffer = calloc (fileLen + 1, 1); 

Trong C, không có nhu cầu để cast giá trị trả về của malloc hoặc calloc. Ở trên sẽ đảm bảo rằng chuỗi sẽ bị vô hiệu hóa ngay cả khi đọc tệp kết thúc sớm vì bất kỳ lý do gì.calloc mất nhiều thời gian hơn malloc bởi vì nó phải bằng không tất cả bộ nhớ bạn đã yêu cầu trước khi đưa nó cho bạn.

+0

@downvoter: cách bạn giải thích – dreamlax

2

cách tiếp cận của bạn để xác định kích thước tập tin bằng cách tìm kiếm đến cuối của tập tin và sau đó sử dụng ftell() là sai:

  • Nếu nó là một tập tin văn bản, mở mà không cần "b" trong tham số thứ hai đến fopen() cuộc gọi, sau đó ftell() có thể không cho bạn biết số ký tự mà bạn có thể đọc từ tệp. Ví dụ: cửa sổ sử dụng hai byte cho cuối dòng, nhưng khi đọc, nó là một trong số char. Trên thực tế, giá trị trả lại của ftell() cho các luồng được mở ở chế độ văn bản chỉ hữu ích trong các cuộc gọi đến fseek() và không xác định kích thước tệp.
  • Nếu nó là một tập tin nhị phân, mở ra với "b" trong tham số thứ hai để fopen(), sau đó tiêu chuẩn C có này để nói:

    Thiết lập vị trí chỉ thị tập tin để kết thúc-of-file, như với fseek(file, 0, SEEK_END), có hành vi không xác định đối với luồng nhị phân (vì có thể có các ký tự null sau) hoặc cho bất kỳ luồng nào có mã hóa phụ thuộc vào trạng thái không chắc chắn kết thúc ở trạng thái thay đổi ban đầu.

Vì vậy, những gì bạn đang làm không nhất thiết phải đi làm việc trong tiêu chuẩn C. Tốt nhất là sử dụng fread() để đọc, và nếu bạn tình cờ cần nhiều bộ nhớ hơn, sử dụng realloc(). Hệ thống của bạn có thể cung cấp mmap() hoặc có thể đảm bảo về việc đặt chỉ báo vị trí tệp thành tệp kết thúc cho luồng nhị phân — nhưng dựa vào những thứ không di động.

Xem thêm C-FAQ này: What's the difference between text and binary I/O?.

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