2012-01-13 33 views
30

Sto usandoCome posso chiudere i file che rimangono aperti dopo un errore?

fid = fopen('fgfg.txt'); 

per aprire un file.

A volte si verifica un errore prima che riesca a chiudere il file. Non posso fare nulla con quel file fino a quando non chiudo Matlab.

Come posso chiudere un file in caso di errore?

+0

Strettamente correlati: [Come gestite le risorse in MATLAB in modo eccezionalmente sicuro? (come "prova ... finalmente")] (http://stackoverflow.com/q/1098149/52738) – gnovice

risposta

49

Prima di tutto, è possibile utilizzare il comando

fclose all 

In secondo luogo, è possibile utilizzare try-catch blocchi e chiudere il file gestisce

try 
    f = fopen('myfile.txt','r') 
    % do something 
    fclose(f); 
catch me 
    fclose(f); 
    rethrow(me); 
end 

C'è un terzo approccio, che è molto meglio. Matlab è ora un linguaggio orientato agli oggetti con Garbage Collector. È possibile definire un oggetto wrapper che si occuperà automaticamente del suo ciclo di vita.

Poiché è possibile in Matlab chiamare metodi di un oggetto sia in questo modo:

myObj.method()

e in questo modo:

metodo (myObj)

È possibile definire una classe che imita tutti del comando file pertinente e incapsula il ciclo di vita.

classdef safefopen < handle 
    properties(Access=private) 
     fid; 
    end 

    methods(Access=public) 
     function this = safefopen(fileName,varargin)    
      this.fid = fopen(fileName,varargin{:}); 
     end 

     function fwrite(this,varargin) 
      fwrite(this.fid,varargin{:}); 
     end 

     function fprintf(this,varargin) 
      fprintf(this.fid,varargin{:}); 
     end 

     function delete(this) 
      fclose(this.fid); 
     end 
    end 

end 

L'operatore eliminare viene chiamato automaticamente dal Matlab. (Ci sono più funzioni che è necessario avvolgere, (fread, fseek, ecc.)).

Così ora avete maniglie sicure che chiudono automaticamente il file se ne avete perso l'ambito o si è verificato un errore.

usare in questo modo:

f = safefopen('myFile.txt','wt') 
fprintf(f,'Hello world!'); 

E non c'è bisogno di chiudere.

Edit: Ho solo pensato di confezionamento fclose() di non fare nulla. Potrebbe essere utile per la compatibilità con le versioni precedenti, per le vecchie funzioni che utilizzano gli ID di file.

Edit (2): A seguito di @AndrewJanke buon commento, vorrei migliorare il metodo di eliminazione gettando errori sul fclose()

function delete(this)   
     [msg,errorId] = fclose(this.fid); 
     if errorId~=0 
      throw(MException('safefopen:ErrorInIO',msg)); 
     end 
    end 
+5

+1 Grande uso di delete. Un problema: questo è I/O bufferizzato, quindi le scritture non riuscite possono comparire solo nella chiamata fclose(); così come sono, saranno silenziosamente ignorati qui. Potrebbe testare il valore di ritorno di fclose() e l'errore di chiamata() in caso di errore. E in 'delete' l'errore si trasformerà in un avvertimento. Potrebbe voler includere un metodo fclose() opzionale in modo che il chiamante possa esporre l'errore come eccezione o gestirlo, e avere delete() chiudi se è ancora aperto.Potrebbe anche voler provare fopen() per errori nel costruttore; così come è, riporterà un fid fid, e quindi errore in fwrite() o delete() quando si agisce su di esso. –

+2

Penso che questo fosse [la mia idea] (http://stackoverflow.com/a/9024064/21322);) In ogni caso, mi assicurerei che il metodo 'delete' sia privo di eccezioni. L'unica cosa che può causare il fallimento di 'fclose' è se' this.fid' è un handle di file non valido; in tal caso, non è necessario chiudere il file. – Nzbuu

+1

@Nzbuu, non discuterò con te sull'originalità :) Mi piace così metto (+1) al tuo post. A proposito, fclose può fallire a causa del fatto che alcuni errori si verificano durante la lettura/scrittura. Dai un'occhiata al commento di AndrewJankes. –

29

Si può provare una "funzione" molto ordinato aggiunto da ML chiamato onCleanup. Loren Shure aveva uno writeup completo su di esso quando è stato aggiunto.È una classe che si istanzia con il tuo codice di pulizia, quindi viene eseguita quando esce dall'ambito di applicazione, ad esempio quando si verifica un errore o la funzione termina. Rende il codice molto pulito. Questa è una versione generica della classe che aveva Andrey sopra. (A proposito, per compiti complessi come colpire le fonti di dati esterne, classi personalizzate sono sicuramente la strada da percorrere.)

da the help:

function fileOpenSafely(fileName) 
    fid = fopen(fileName, 'w'); 
    c = onCleanup(@()fclose(fid)); 

    functionThatMayError(fid); 
end % c executes fclose(fid) here 

In sostanza, si dà un function handle (in questo caso @()fclose(fid)) che funziona quando esce dal campo di applicazione.

Il codice di pulizia viene eseguito quando viene emesso un errore OPPURE quando esce normalmente, poiché si esce da fileOpenSafely e c si spegne.

No try/catch o codice condizionale necessario.

+1

+1 perché non sapevo su onCleanup –

+3

+1 onCleanup è ottimo. In realtà, il file I/O * è * una di quelle attività complesse che sta colpendo un'origine dati esterna. L'I/O di handle di file di Matlab utilizza principalmente i codici di stato invece di generare errori, quindi ogni chiamata a fopen(), fread(), fclose() e così via richiede un controllo di accompagnamento del valore di ritorno o ferror() per essere completamente corretti . Ed è fastidioso, quindi nessuno si preoccupa di farlo davvero nel loro codice. È perfetto per una classe personalizzata che li avvolge e aggiunge controlli di stato che si rivolgono alle chiamate error() in caso di errore. –

+1

Andrew: per il file io che esegui regolarmente, sono d'accordo, ma per uno script veloce e sporco, questo è un modo per accelerare il tuo gioco facilmente. – Marc

4

Andrey La soluzione above è davvero l'approccio migliore a questo problema. Volevo solo aggiungere che il lancio di un'eccezione nel metodo delete() potrebbe essere problematico se si gestiscono array di oggetti safefopen. Durante la distruzione di tale array, MATLAB chiamerà delete() su ogni elemento dell'array e, se ci sono dei lanci delete(), si potrebbe finire con gli handle di file aperti rimanenti. Se davvero hai bisogno di sapere se qualcosa è andato storto durante la distruzione, emettere un avvertimento sarebbe una scelta migliore.

Per coloro che si sentono pigro per scrivere tutti i metodi di inoltro ad ogni integrato MATLAB che utilizza gli handle di file, si può considerare la semplice alternativa del metodo di subsref sovraccarico per la classe safefopen:

methods(Access=public) 
    function varargout = subsref(this, s)    
     switch s(1).type     
      case '.'      
       if numel(s) > 1, 
        feval(s(1).subs, this.fid, s(2).subs{:}); 
       else 
        feval(s(1).subs, this.fid); 
       end 
       % We ignore outputs, but see below for an ugly solution to this 
       varargout = {}; 
      otherwise      
       varargout{1} = builtin('subsref', this, s);      
     end  

    end 
end 

Questa alternativa utilizza la po brutto feval, ma ha il vantaggio di funzionare anche se i ragazzi di MATLAB (o te stesso) decidono di aggiungere nuove funzioni che coinvolgono gli handle di file o se il numero/ordine degli argomenti di input per una determinata funzione cambia. Se si decide di andare per la subsref alternativa poi si dovrebbe utilizzare la classe safefopen come questo:

myFile = safefopen('myfile.txt', 'w'); 
myFile.fprintf('Hello World!'); 

EDIT: Uno svantaggio della soluzione subsref è che non tiene conto tutti gli argomenti di uscita. Se avete bisogno degli argomenti di output allora si dovrà introdurre un po 'di bruttezza:

methods(Access=public) 
function varargout = subsref(this, s)     
     if nargout > 0, 
      lhs = 'varargout{%d} '; 
      lhs = repmat(lhs, 1, nargout); 
      lhs = ['[' sprintf(lhs, 1:nargout) ']=']; 
     else 
      lhs = ''; 
     end    
     switch s(1).type     
      case '.'      
       if numel(s) > 1,       
        eval(... 
         sprintf(... 
         '%sfeval(''%s'', this.fid, s(2).subs{:});', ... 
         lhs, s(1).subs) ... 
         );       
       else       
        eval(... 
         sprintf('%sfeval(''%s'', this.fid);', ... 
         lhs, s(1).subs) ... 
         );       
       end     

      otherwise      
       varargout{1} = builtin('subsref', this, s); 

     end    
end 
end 

E allora si potrebbe fare le cose come:

myFile = safefopen('myfile.txt', 'w'); 
count = myFile.fprintf('Hello World!'); 
[filename,permission,machineformat,encoding] = myFile.fopen(); 
+1

+1 - Fantastico! Mi è piaciuto l'approccio feval. Inoltre sono d'accordo con il tuo commento sugli array di file. –

1
fids=fopen('all'); 
fclose(fids); 

% partendo dal presupposto che si desidera chiudere tutti aperti filehandles

Problemi correlati