2012-01-13 2 views
30

Я используюКак закрыть файлы, которые остаются открытыми после ошибки?

fid = fopen('fgfg.txt'); 

открыть файл.

Иногда возникает ошибка, прежде чем мне удастся закрыть файл. Я не могу ничего сделать с этим файлом, пока не закрою Matlab.

Как закрыть файл, если возникла ошибка?

+0

Близко связанные: [Как вы обрабатываете ресурсы в MATLAB безопасным образом? (например, «try ... finally»)] (http://stackoverflow.com/q/1098149/52738) – gnovice

ответ

49

Прежде всего, вы можете использовать команду

fclose all 

Во-вторых, вы можете использовать примерку уловы блоков и закройте файл ручки

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

Существует третий подход, который гораздо лучше. Matlab теперь является объектно-ориентированным языком с сборщиком мусора. Вы можете определить объект-оболочку, который автоматически позаботится о его жизненном цикле.

Так как это возможно в Matlab для вызова методов объекта как в этом случае:

myObj.method()

и таким образом:

метод (myObj)

Вы можете определить класс, имитирующий все соответствующей команды файла и инкапсулирует жизненный цикл.

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 

удалить оператор автоматически вызывается Matlab. (Есть больше функций, которые вам нужно будет обернуть (fread, fseek и т. Д.)).

Итак, теперь у вас есть безопасные дескрипторы, которые автоматически закрывают файл, потеряли ли вы его объем или произошла ошибка.

Используйте это так:

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

И нет необходимости закрывать.

Edit: Я просто подумал о оберточной fclose() ничего не делать. Это может быть полезно для обратной совместимости - для старых функций, которые используют идентификаторы файлов.

Edit (2): После @AndrewJanke хороший комментарий, я хотел бы улучшить метод удаления бросали ошибки на fclose()

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

+1 Большое использование удаления. Один улов: это буферизованный ввод-вывод, поэтому неудачные записи могут отображаться только в вызове fclose(); как есть, они будут молча игнорироваться здесь. Мог бы проверить возвращаемое значение fclose() и вызвать ошибку() при сбое. И в 'delete' ошибка превратится в предупреждение. Может захотеть включить необязательный метод fclose(), чтобы вызывающий мог выставить ошибку в качестве исключения или обработать ее, а delete() просто закрыть, если он все еще открыт.Может также хотеть проверить fopen() на отказ в конструкторе; как есть, он будет спрятать недопустимый fid, а затем ошибку в fwrite() или delete() при действии на него. –

+2

Я думаю, что это была [моя идея] (http://stackoverflow.com/a/9024064/21322);) В любом случае я бы удостоверился, что метод 'delete' исключен. Единственное, что может привести к сбою 'fclose' - это если' this.fid' является недопустимым дескриптором файла; в этом случае вам не нужно закрывать файл. – Nzbuu

+1

@Nzbuu, я не буду спорить с тобой об оригинальности :) Мне нравится, поэтому я положил (+1) на свой пост. Кстати, fclose может выйти из строя из-за того, что некоторые ошибки происходят во время чтения/записи. Просмотрите комментарий AndrewJankes. –

29

Вы можете попробовать очень аккуратно "функцию" добавлено ML под названием onCleanup. У Loren Shure был полный writeup на нем, когда он был добавлен.Это класс, который вы создаете с помощью кода очистки, затем он выполняется, когда он выходит за пределы области видимости - то есть когда это происходит с ошибками или заканчивается функция. Делает код очень чистым. Это общая версия класса, которая была выше Andrey. (Кстати, для сложных задач, таких как удары внешних источников данных, пользовательские классы, безусловно, путь.)

из the help:

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

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

В принципе, вы дать ему function handle (в данном случае @()fclose(fid)), что это запускается, когда он выходит за рамки.

Ваш код очистки выполняется либо при ошибке, либо когда он выходит из строя, потому что вы выходите из fileOpenSafely и c выходит за рамки.

No try/catch или условный код необходим.

+1

+1, потому что я не знал о onCleanup –

+3

+1 onCleanup отлично. Но на самом деле, файл ввода/вывода * является * одним из тех сложных задач, которые попадают в внешний источник данных. Обработчик дескриптора файла Matlab в основном использует коды состояния, а не бросает ошибки, поэтому каждый вызов функции fopen(), fread(), fclose() и т. Д. Нуждается в сопроводительной проверке возвращаемого значения или ferror(), чтобы быть полностью правильным , И это раздражает, поэтому никто не мешает на самом деле делать это в своем коде. Он идеально подходит для настраиваемого класса, который их обертывает и добавляет проверки состояния, которые включаются в вызовы error() при сбое. –

+1

Andrew - для файла io, который вы делаете регулярно, я соглашусь, но для быстрого и грязного сценария это способ легко увеличить вашу игру. – Marc

4

Andrey решение above действительно лучший подход к решению этой проблемы. Я просто хотел добавить, что исключение исключения в методе delete() может быть проблематичным, если вы имеете дело с массивами объектов safefopen. Во время уничтожения такого массива MATLAB будет вызывать delete() для каждого элемента массива и, если возникнет какая-либо ошибка delete(), вы можете получить оставшиеся открытые дескрипторы файлов. Если вам действительно нужно знать, что что-то пошло не так во время разрушения, то выдача предупреждения будет лучшим вариантом ИМХО.

Для тех, лень писать все методы пересылки каждому MATLAB встроенной команды, которая использует дескрипторы файлов, вы можете рассмотреть простой вариант перегрузки метода subsref для класса 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 

Эта альтернатива использует несколько ugly feval, но имеет преимущество в работе, даже если ребята MATLAB (или самостоятельно) решили добавить новые функции, которые связаны с файловыми дескрипторами, или если число/порядок входных аргументов для данной функции изменится. Если вы решили пойти на subsref альтернатив, то вы должны использовать класс safefopen так:

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

EDIT: Недостатком subsref решения является то, что он игнорирует все аргументы вывода. Если вам нужны выходные аргументы, то вам придется ввести еще некоторые уродства:

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 

И тогда вы могли бы сделать что-то вроде:

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

+1 - Круто! Мне понравился подход feval. Также я согласен с вашим комментарием о массивах файлов. –

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

% при условии, что вы хотите, чтобы закрыть все открытые filehandles