Como posso fechar arquivos que são deixados abertos após um erro?

estou usando

fid = fopen('fgfg.txt'); 

para abrir um arquivo.

Às vezes, um erro ocorre antes de eu conseguir fechar o arquivo. Eu não posso fazer nada com esse arquivo até que eu feche o Matlab.

Como posso fechar um arquivo se ocorrer um erro?

Primeiro de tudo, você pode usar o comando

 fclose all 

Em segundo lugar, você pode usar blocos try-catch e fechar suas alças de arquivo

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

Existe uma terceira abordagem, que é muito melhor. O Matlab agora é uma linguagem orientada a objects com o coletor de lixo. Você pode definir um object wrapper que cuidará de seu ciclo de vida automaticamente.

Já que é possível no Matlab chamar os methods de objects desta maneira:

myObj.method ()

e assim:

método (myObj)

Você pode definir uma class que imita todo o comando de arquivo relevante e encapsula o ciclo de vida.

 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 

O operador delete é chamado automaticamente pelo Matlab. (Há mais funções que você precisará para embrulhar, (fread, fseek, etc.)).

Então agora você tem alças seguras que fecham automaticamente o arquivo se você perdeu o escopo dele ou se ocorreu um erro.

Use assim:

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

E não precisa fechar.

Edit: Eu só pensei em envolver fclose() para não fazer nada. Pode ser útil para compatibilidade com versões anteriores - para funções antigas que usam ids de arquivo.

Editar (2): Após @AndrewJanke bom comentário, gostaria de melhorar o método de exclusão jogando erros em fclose ()

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

Você pode tentar uma “function” muito legal adicionada pelo ML chamado onCleanup . Loren Shure teve um writeup completo quando foi adicionado. É uma class que você instancia com seu código de limpeza, então é executada quando sai do escopo – isto é, quando ocorre um erro ou quando a function termina. Torna o código muito limpo. Esta é uma versão genérica da class que Andrey tinha acima. (BTW, para tarefas complexas como acertar fonts de dados externas, classs personalizadas são definitivamente o caminho a percorrer.)

da ajuda:

 function fileOpenSafely(fileName) fid = fopen(fileName, 'w'); c = onCleanup(@()fclose(fid)); functionThatMayError(fid); end % c executes fclose(fid) here 

Basicamente, você dá a ele um identificador de function (neste caso, @()fclose(fid) ) que é executado quando sai do escopo.

Seu código de limpeza é executado quando um erro é lançado OU quando ele sai normalmente, porque você sai do fileOpenSafely e c sai do escopo.

Nenhum try/catch ou código condicional necessário.

A solução de Andrey acima é de fato a melhor abordagem para esse problema. Eu só queria acrescentar que lançar uma exceção no método delete() poderia ser problemático, se você lida com matrizes de objects safefopen . Durante a destruição de uma matriz, o MATLAB chamará delete() em cada elemento da matriz e, se qualquer delete() lançado, você poderá acabar com as sobras de identificadores de arquivos abertos. Se você realmente precisa saber se algo deu errado durante a destruição, em seguida, emitir um aviso seria uma melhor opção IMHO.

Para aqueles que se sentem preguiçosos em escrever todos os methods de encaminhamento para cada MATLAB embutido que usa identificadores de arquivo, você pode considerar a alternativa simples de subsref método de sobrecarga para a class 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 

Essa alternativa usa o feval um tanto feio, mas tem a vantagem de funcionar mesmo se o pessoal do MATLAB (ou você mesmo) decidir adicionar novas funções que envolvem handles de arquivo, ou se o número / ordem dos argumentos de input para uma determinada function mudar. Se você decidir ir para a alternativa de subsref então você deve usar a class safefopen assim:

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

EDIT: Uma desvantagem da solução de subsref é que desconsidera todos os argumentos de saída. Se você precisa dos argumentos de saída, então você terá que introduzir um pouco mais de feiura:

 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 então você poderia fazer coisas como:

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

% supondo que você deseja fechar todos os filehandles abertos