2009-12-23 14 views
25

È necessario verificare il valore di ritorno di fclose? Se abbiamo aperto con successo un file, quali sono le possibilità che potrebbe non riuscire a chiudere?fclose controllo del valore di ritorno

Grazie!

saluti, Jay

+0

Grazie a tutti per tutte le risposte! :) – Jay

+0

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

+1

È necessario considerare la marcatura uno come corretto. – caf

risposta

32

Quando si fwrite in un file, è possibile che in realtà non scrivere nulla, si possono alloggiare in un buffer (all'interno dell'oggetto FILE). Chiamare lo fflush lo scriverà sul disco. Questa operazione potrebbe non riuscire, ad esempio se hai appena finito lo spazio su disco o c'è qualche altro errore I/O.

fclose svuota implicitamente anche i buffer, pertanto potrebbe non riuscire per gli stessi motivi.

1

La pagina man fclose cita che potrebbe non riuscire per uno dei motivi per cui una chiusura o una chiusura non riescono.

Citando:

Il close() chiamata di sistema fallirà se:

[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 può fallire per ragioni che write() fallirebbe, in pratica nel caso in cui si può in realtà scrivere/salvare il file.

0

Se, nel contesto dell'applicazione, è possibile pensare a qualcosa di utile da fare se fclose() fallisce, quindi verificare il valore restituito. Se non puoi, non farlo.

+0

OK ma non hai fornito un esempio in cui è necessario controllare! – AraK

+0

Non vedere che la domanda è stata chiederne una. –

+0

Non dice: "quali sono le possibilità che non riesca a chiudere?" – AraK

2

È necessario verificare sempre frutto di fclose()

1

Una ragione fclose può non è se ci sono dati ancora nel buffer e il fflush implicita fallisce. Quello che consiglio è di chiamare sempre fflush e fare qualsiasi errore nella gestione di lì.

+0

In cosa consiste questa gestione degli errori? –

+0

@Neil - lo stesso errore di gestione che l'app farebbe se un'operazione di scrittura fallisse (fallire la richiesta, registrare un errore, ecc.). –

1

Ho visto molte volte fclose() restituire non-zero.

E su un attento esame è emerso che il problema reale era con la scrittura e non fclose.

Poiché il materiale in fase di scrittura viene memorizzato nel buffer prima che avvenga la scrittura effettiva e quando viene chiamato un fclose() viene scaricato tutto il buffer. Quindi qualsiasi problema nella scrittura del suff di buffer, ad esempio come il disco pieno, appare durante fclose(). Come dice David Yell, per scrivere un'applicazione a prova di proiettile è necessario considerare il valore restituito da fclose().

+0

da aggiungere: la funzione restituisce zero in caso di successo o EOF in caso di fallimento –

3

Si potrebbe (e dovrebbe) segnalare l'errore, ma in un certo senso, il stream is still closed:

Dopo la chiamata a fclose(), qualsiasi uso dei risultati del flusso in un comportamento indefinito.

3
  1. fclose() sarà svuotare qualsiasi uscita non scritta (via fflush()) prima di ritornare, quindi i risultati di errore dal sottostante write() non saranno segnalati al fwrite() o fprintf() tempo, ma quando si fa il fclose().Come risultato, qualsiasi errore che write() o fflush() può generare può essere generato da fclose().

  2. fclose() sarà anche chiamare close() che può generare errori sul client NFS, in cui il file modificato non viene effettivamente caricato sul server remoto finché close() tempo. Se il server NFS è caduto, poi il close() avrà esito negativo, e quindi non riuscirà pure fclose(). Questo potrebbe essere vero per altri file system in rete.

26

Da comp.lang.c:

La chiamata fclose() può fallire, e deve essere solo come assiduamente come tutte le altre operazioni sui file errore-controllato. Suoni pedanti, giusto? Sbagliato. In una vita precedente , prodotto della mia azienda è riuscito a distruggere i dati di un cliente omettendo un assegno di fallimento quando chiusura di un file. La sequenza è andato qualcosa di simile (parafrasato):

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

Naturalmente, quello che è successo è stato che fclose() ha esaurito lo spazio su disco cercando di scrivere l'ultimo paio di blocchi di dati , in modo che il `tempfile' è stato troncato e inutilizzabile. E poiché l'errore fclose() non è stato rilevato, il programma è andato avanti e ha distrutto la versione migliore esistente dei dati in favore della versione danneggiata. E, come Murphy ha voluto che, la vittima in questo particolare incidente è stata la responsabile del reparto del cliente, la persona con l'autorità di acquistare più del nostro prodotto o sostituire con un prodotto della concorrenza - e, naturalmente, una persona che era già insoddisfatta di noi per altri motivi.

sarebbe una forzatura attribuire tutti la miseria conseguente a questo singolo omissione, ma può valere la pena di puntamento che sia il cliente che il mio ex società da allora sono scomparsi dal ecologia aziendale.

controllare quelli codici di errore!

+0

Haha. Anch'io ho pensato a quel post quando ho letto la domanda. Ho deciso di non cercarlo però, e ne ho scritto a memoria. –

+0

@Alok: Ottimo post :) .. otto anni ma suona ancora vero –

2

Diciamo che stai generando dati. Si dispone di dati vecchi che si fread() da un file, e poi fare un po 'di elaborazione sui dati, generano più dati, e poi scrivere in un nuovo file. Stai attento a non sovrascrivere il vecchio file perché sai che il tentativo di creare un nuovo file potrebbe non riuscire e vorresti conservare i vecchi dati in quel caso (alcuni dati sono migliori di nessun dato). Dopo aver terminato tutte le fwrite() s che hanno lo scopo (perché si meticolosamente controllato il valore restituito da fwrite()), si fclose() il file. Quindi, si rename() il proprio file appena scritto e si sovrascrive il vecchio file.

Se fclose() non è riuscito a causa di un errore di scrittura (disco pieno?), Hai appena sovrascritto il tuo ultimo file valido con qualcosa che potrebbe essere spazzatura. Ops.

Quindi, se è fondamentale, è necessario controllare il valore restituito di fclose().

in termini di codice:

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

nel codice precedente, siamo stati attenti a fopen(), fwrite(), e fread(), ma anche allora, non controllare fclose() può causare la perdita di dati (quando si compila con MAYLOSE_DATA definito).

+0

sarebbe bello se tu dessi un esempio di come controllare il valore restituito per completezza –

+0

Fatto. Ho fatto un'opzione in fase di compilazione per eseguire il codice giusto. –

+0

Ottimo! Tranne che hai dimenticato di controllare un paio di altre affermazioni :) –

0

In un certo senso, closing a file never fails: gli errori vengono restituiti se un'operazione di scrittura in attesa non è riuscita, ma lo streaming sarà da chiudere.

Per evitare problemi e garantire (per quanto è possibile da un programma C), vi consiglio:

  1. gestire correttamente gli errori restituiti da fwrite().
  2. Chiama fflush() prima di chiudere il flusso. Do Ricordarsi di verificare gli errori restituiti da fflush().