2012-01-13 14 views
30

estoy usando¿Cómo puedo cerrar los archivos que quedan abiertos después de un error?

fid = fopen('fgfg.txt'); 

para abrir un archivo.

A veces ocurre un error antes de que logre cerrar el archivo. No puedo hacer nada con ese archivo hasta que cierre Matlab.

¿Cómo puedo cerrar un archivo si ocurre un error?

+0

Relacionado estrechamente: [¿Cómo se manejan los recursos en MATLAB de una manera segura y excepcional? (como "try ... finally")] (http://stackoverflow.com/q/1098149/52738) – gnovice

Respuesta

49

En primer lugar, puede utilizar el comando

fclose all 

En segundo lugar, puede utilizar try-catch bloques y cerrar el archivo se encarga de

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

Hay un tercer enfoque, que es mucho mejor. Matlab ahora es un lenguaje orientado a objetos con el recolector de basura. Puede definir un objeto envoltorio que se ocupará automáticamente de su ciclo de vida.

Dado que es posible en Matlab para llamar a los métodos de objeto tanto de esta manera:

myObj.method()

y de esa manera:

método

(myObj)

Puede definir una clase que imite todo del comando de archivo relevante, y encapsula el 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 

El operador eliminar es llamado automáticamente por Matlab. (Hay más funciones que deberá envolver, (fread, fseek, etc.)).

Así que ahora tiene controladores seguros que cierran automáticamente el archivo ya sea que haya perdido alcance o haya ocurrido un error.

utilizar de esta manera:

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

Y no hay necesidad de cerrar.

Editar: solo pensaba en envolver fclose() no hacer nada. Puede ser útil para compatibilidad con versiones anteriores: para funciones antiguas que usan ID de archivos.

Editar (2): Siguiendo @AndrewJanke buen comentario, me gustaría mejorar el método de eliminación por tirar errores en fclose()

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

+1 Gran uso de eliminar. Una pega: esto es E/S con búfer, por lo que las escrituras fallidas solo pueden aparecer en la llamada a fclose(); como es, serán silenciosamente ignorados aquí. Podría probar el valor de retorno de fclose() y el error de llamada() si falla. Y en 'delete' el error se convertirá en una advertencia. Puede querer incluir un método opcional fclose() para que la persona que llama pueda exponer el error como una excepción o manejarlo, y tenga delete() solo cierre si todavía está abierto.También podría querer probar fopen() por falla en el constructor; como está, ocultará un fid inválido, y luego un error en fwrite() o delete() cuando actúe sobre él. –

+2

Creo que esta fue [mi idea] (http://stackoverflow.com/a/9024064/21322);) En cualquier caso, me aseguraré de que el método 'delete' esté libre de excepciones. Lo único que puede hacer que 'fclose' falle es si' this.fid' es un manejador de archivo no válido; en ese caso, no es necesario que cierre el archivo. – Nzbuu

+1

@Nzbuu, no voy a discutir con usted sobre la originalidad :) Me gusta, así que puse (+1) en su publicación. Por cierto, fclose puede fallar debido al hecho de que algunos errores ocurren durante la lectura/escritura. Echa un vistazo al comentario de AndrewJankes. –

29

Usted puede tratar de una "función" muy ordenado agregado por ML llamado onCleanup. Loren Shure tenía un writeup completo cuando se lo agregó.Es una clase que instancia con su código de limpieza, luego se ejecuta cuando se sale del alcance, es decir, cuando se produce un error o la función finaliza. Hace el código muy limpio. Esta es una versión genérica de la clase que tenía Andrey anteriormente. (Por cierto, para tareas complejas, como golpear a fuentes de datos externas, clases personalizadas son definitivamente el camino a seguir.)

de the help:

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

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

Básicamente, se le da un function handle (en este caso @()fclose(fid)) que se ejecuta cuando sale del alcance.

Su código de limpieza se ejecuta cuando se produce un error O cuando sale normalmente, porque sale de fileOpenSafely y c sale del alcance.

No try/catch o código condicional necesario.

+1

+1 porque no sabía acerca de onCleanup –

+3

+1 onCleanup es genial. Pero en realidad, la E/S de archivos * es * una de esas tareas complejas que afectan a una fuente de datos externa. La E/S de identificador de archivos de Matlab usa códigos de estado en lugar de errores, por lo que cada llamada a fopen(), fread(), fclose(), etc. necesita una verificación acompañante del valor devuelto o ferror() para ser completamente correcto . Y es molesto, por lo que nadie se molesta en hacerlo en su código. Es perfecto para una clase personalizada que los envuelve y agrega controles de estado que se convierten en llamadas de error() cuando falla. –

+1

Andrew: para el archivo io que haces rutinariamente, estoy de acuerdo, pero para un script rápido y sucio, esta es una manera de intensificar tu juego fácilmente. – Marc

4

Andrey La solución above es de hecho el mejor enfoque para este problema. Solo quería agregar que lanzar una excepción en el método delete() podría ser problemático si se trata con matrices de objetos safefopen. Durante la destrucción de dicha matriz, MATLAB llamará al delete() en cada elemento de la matriz y, si se lanza delete(), puede terminar con los sobrantes de los archivos abiertos. Si realmente necesita saber si algo salió mal durante la destrucción, emitir una advertencia sería una mejor opción en mi humilde opinión.

Para aquellos que se sienten vago para escribir todos los métodos de reenvío a cada orden interna de MATLAB que utiliza identificadores de archivos, puede considerar la alternativa simple de la sobrecarga de métodos subsref para la clase 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 

Esta alternativa utiliza el algo ugly feval, pero tiene la ventaja de funcionar incluso si los chicos de MATLAB (o usted mismo) deciden agregar nuevas funciones que involucren manejadores de archivos, o si el número/orden de los argumentos de entrada a una función determinada cambia. Si usted decide ir a por la alternativa subsref se debe usar la clase safefopen así:

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

EDIT: Una desventaja de la solución subsref es que no tiene en cuenta todos los argumentos de salida. Si necesita los argumentos de salida a continuación, tendrá que introducir un poco más la fealdad:

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 

Y entonces se podría hacer cosas como:

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

+1 - ¡Genial! Me gustó el enfoque feval. También estoy de acuerdo con su comentario sobre las matrices de archivos. –

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

%, suponiendo que desea cerrar todas abiertas Filehandles

Cuestiones relacionadas