2009-12-23 37 views
25

Có cần kiểm tra giá trị trả lại của fclose không? Nếu chúng tôi đã mở thành công một tệp, cơ hội mà nó có thể không đóng được là gì?fclose kiểm tra giá trị trả lại

Cảm ơn!

Kính trọng, Jay

+0

Cảm ơn mọi người vì tất cả các câu trả lời! :) – Jay

+0

cũng xem: http://stackoverflow.com/questions/569573/what-is-a-good-programming-pattern-for-handling-return-values-from-stdio-file-wri –

+1

Bạn nên cân nhắc việc đánh dấu một cái đúng. – caf

Trả lời

32

Khi bạn fwrite vào một tập tin, nó có thể không thực sự viết bất cứ điều gì, nó có thể ở lại trong một bộ đệm (bên trong đối tượng FILE). Gọi fflush thực sự sẽ ghi nó vào đĩa. Thao tác đó có thể thất bại, ví dụ nếu bạn vừa hết dung lượng đĩa hoặc có một số lỗi I/O khác.

fclose đồng thời xóa bộ đệm, vì vậy có thể không thành công vì các lý do tương tự.

1

Trang người đàn ông fclose trích dẫn rằng nó có thể không thành công vì bất kỳ lý do nào gần hoặc fflush có thể không thành công.

Trích dẫn:

Mức giá đóng cửa() hệ thống gọi sẽ thất bại nếu:

[EBADF]   fildes is not a valid, active file descriptor. 

[EINTR]   Its execution was interrupted by a signal. 

[EIO]    A previously-uncommitted write(2) encountered an 
        input/output error. 

fflush có thể thất bại vì những lý do mà write() sẽ thất bại, về cơ bản trong trường hợp mà bạn có thể 't thực sự ghi vào/lưu tập tin.

0

Nếu, trong ngữ cảnh ứng dụng của bạn, bạn có thể nghĩ ra điều gì đó hữu ích để làm nếu fclose() không thành công, sau đó kiểm tra giá trị trả lại. Nếu bạn không thể, đừng.

+0

OK nhưng bạn không đưa ra một ví dụ mà bạn cần kiểm tra! – AraK

+0

Không thấy rằng câu hỏi đã được yêu cầu. –

+0

Anh ta không nói: "cơ hội mà nó có thể không đóng được là gì?" – AraK

2

Bạn luôn luôn phải kiểm tra kết quả của fclose()

1

Một lý do fclose có thể thất bại là nếu có bất kỳ dữ liệu vẫn đệm và fflush ngầm thất bại. Những gì tôi khuyên bạn nên luôn luôn gọi fflush và làm bất kỳ lỗi xử lý ở đó.

+0

Việc xử lý lỗi đó bao gồm những gì? –

+0

@Neil - cùng một lỗi khi xử lý ứng dụng sẽ thực hiện nếu thao tác ghi không thành công (không thực hiện được yêu cầu, đăng nhập lỗi, v.v.). –

1

Tôi đã nhìn thấy nhiều lần fclose() trả về khác 0.

Và trên kiểm tra cẩn thận phát hiện ra rằng vấn đề thực tế là với việc viết và không fclose.

Vì nội dung đang được ghi đang được đệm trước khi ghi thực tế xảy ra và khi một fclose() được gọi là tất cả bộ đệm bị xóa. Vì vậy, bất kỳ vấn đề bằng văn bản của các buffered suff, nói như đĩa đầy đủ, xuất hiện trong fclose(). Như David Yell nói, để viết một ứng dụng chống đạn, bạn cần xem xét giá trị trả về của fclose().

+0

để thêm: Hàm trả về số không thành công hoặc EOF khi lỗi –

3

Bạn có thể (và nên) báo cáo lỗi, nhưng trong một nghĩa nào đó, stream is still closed:

Sau khi cuộc gọi đến fclose(), việc sử dụng kết quả hoạt động vào hành vi không xác định.

3
  1. fclose() sẽ tuôn ra bất kỳ bất thành văn (thông qua fflush()) trước khi trở về, vì vậy kết quả lỗi từ cơ bản write() sẽ không được báo cáo tại fwrite() hoặc fprintf() thời gian, nhưng khi bạn làm fclose().Kết quả là, bất kỳ lỗi nào mà có thể tạo ra write() hoặc fflush() có thể được tạo bởi fclose().

  2. fclose() cũng sẽ gọi close() mà có thể tạo ra các lỗi trên NFS khách hàng, nơi các tập tin thay đổi không thực sự được tải lên máy chủ từ xa cho đến khi close() thời gian. Nếu máy chủ NFS bị lỗi, thì close() sẽ không thành công, và do đó fclose() cũng sẽ không thành công. Điều này có thể đúng với các hệ thống tệp được nối mạng khác.

26

Từ comp.lang.c:

Các fclose() cuộc gọi có thể thất bại, và nên có lỗi kiểm tra cũng giống như một cách cần mẫn như tất cả các hoạt động tập tin khác. Nghe có vẻ ngớ ngẩn, đúng không? Sai rồi. Trong một cuộc sống trước đây , sản phẩm của công ty tôi được quản lý để hủy dữ liệu của khách hàng bằng cách bỏ qua kiểm tra lỗi khi đóng một tệp. Trình tự đi một cái gì đó tương tự (diễn giải):

stream = fopen(tempfile, "w"); 
if (stream == NULL) ... 
    while (more_to_write) 
     if (fwrite(buffer, 1, buflen, stream) != buflen) ... 
fclose (stream); 

/* The new version has been written successfully. Delete 
* the old one and rename. 
*/ 
remove (realfile); 
rename (tempfile, realfile); 

Tất nhiên, những gì đã xảy ra là fclose() chạy ra khỏi không gian đĩa cố gắng để viết vài khối cuối cùng của dữ liệu, do đó 'tempfile' đã bị cắt bớt và không sử dụng được. Và kể từ khi fclose() thất bại không được phát hiện, chương trình đã đi ngay trước và phá hủy phiên bản tốt nhất còn tồn tại của dữ liệu trong ưu tiên của phiên bản bị hỏng. Và, như Murphy sẽ có nó, nạn nhân trong biến cố hi hữu này là người phụ trách bộ phận của khách hàng, người có thẩm quyền để mua thêm các sản phẩm của chúng tôi hoặc thay thế nó với sản phẩm của đối thủ cạnh tranh - và, natch, một người đã là không hài lòng với chúng tôi vì những lý do khác.

Nó muốn được một đoạn để gán cho tất cả các đau khổ tiếp theo để thiếu sót duy nhất này, nhưng nó có thể là giá trị chỉ ra rằng cả khách hàng và cựu công ty của tôi đã biến mất kể từ từ hệ sinh thái của công ty.

KIỂM TRA CÁC LOẠI KHÔNG CẦN!

+0

Haha. Tôi cũng nghĩ về bài viết đó khi tôi đọc câu hỏi. Tôi quyết định không tìm kiếm nó, và viết về nó từ bộ nhớ. –

+0

@ Alok: Great post :) .. tám tuổi nhưng nó vẫn còn đúng –

2

Giả sử bạn đang tạo dữ liệu. Bạn có dữ liệu cũ mà bạn fread() từ một tệp và sau đó thực hiện một số xử lý trên dữ liệu, tạo thêm dữ liệu và sau đó ghi nó vào tệp mới. Bạn cẩn thận không ghi đè tệp cũ vì bạn biết rằng việc cố gắng tạo tệp mới có thể không thành công và bạn muốn giữ dữ liệu cũ trong trường hợp đó (một số dữ liệu tốt hơn không có dữ liệu). Sau khi hoàn thành tất cả các fwrite() s, tất cả đều thành công (vì bạn đã kiểm tra tỉ mỉ giá trị trả lại từ fwrite()), bạn fclose() tệp. Sau đó, bạn rename() tệp chỉ được viết và ghi đè tệp cũ.

Nếu fclose() không thành công do lỗi ghi (đĩa đầy?), Bạn chỉ ghi đè tệp tốt cuối cùng của mình bằng thứ gì đó có thể là rác. Rất tiếc.

Vì vậy, nếu quan trọng, bạn nên kiểm tra giá trị trả lại của fclose().

Về mã:

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

int main(void) 
{ 
    FILE *ifp = fopen("in.dat", "rb"); 
    FILE *ofp = fopen("out.dat", "wb"); 
    char buf[BUFSIZ]; 
    size_t n; 
    int success = 1; 

    if (ifp == NULL) { 
     fprintf(stderr, "error opening in.dat\n"); 
     perror("in.dat"); 
     return EXIT_FAILURE; 
    } 

    if (ofp == NULL) { 
     fclose(ifp); 
     fprintf(stderr, "error opening out.dat\n"); 
     perror("out.dat"); 
     return EXIT_FAILURE; 
    } 

    while ((n = fread(buf, 1, sizeof buf, ifp)) > 0) { 
     size_t nw; 
     if ((nw = fwrite(buf, 1, n, ofp)) != n) { 
      fprintf(stderr, "error writing, wrote %lu bytes instead of %lu\n", 
          (unsigned long)n, 
          (unsigned long)nw); 
      fclose(ifp); 
      fclose(ofp); 
      return EXIT_FAILURE; 
     } 
    } 
    if (ferror(ifp)) { 
     fprintf(stderr, "ferror on ifp\n"); 
     fclose(ofp); 
     fclose(ifp); 
     return EXIT_FAILURE; 
    } 

#ifdef MAYLOSE_DATA 
    fclose(ofp); 
    fclose(ifp); 
    rename("out.dat", "in.dat"); /* Oops, may lose data */ 
#else 
    if (fclose(ofp) == EOF) { 
     perror("out.dat"); 
     success = 0; 
    } 
    if (fclose(ifp) == EOF) { 
     perror("in.dat"); 
     success = 0; 
    } 
    if (success) { 
     rename("out.dat", "in.dat"); /* Good */ 
    } 
#endif 
    return EXIT_SUCCESS; 
} 

Trong đoạn mã trên, chúng tôi đã cẩn thận về fopen(), fwrite(), và fread(), nhưng thậm chí sau đó, không kiểm tra fclose() có thể dẫn đến mất dữ liệu (khi biên soạn với MAYLOSE_DATA được xác định).

+0

nó sẽ là tuyệt vời nếu bạn đưa ra một ví dụ về làm thế nào để kiểm tra giá trị trả lại cho đầy đủ –

+0

Xong. Tôi đã làm cho nó một tùy chọn biên dịch thời gian để chạy đúng mã. –

+0

Tuyệt vời! Ngoại trừ bạn quên kiểm tra thêm một vài câu lệnh :) –

0

Theo nghĩa, closing a file never fails: lỗi được trả về nếu thao tác ghi đang chờ xử lý không thành công, nhưng luồng sẽ bị đóng.

Để tránh vấn đề và đảm bảo (như xa như bạn có thể từ một chương trình C), tôi đề nghị bạn:

  1. đúng xử lý lỗi được trả về bởi fwrite().
  2. Gọi fflush() trước khi đóng luồng. Làm nhớ để kiểm tra lỗi được trả lại bởi fflush().
Các vấn đề liên quan