Как я могу закрыть файлы, которые остаются открытыми после ошибки? - PullRequest
30 голосов
/ 13 января 2012

Я использую

fid = fopen('fgfg.txt');

, чтобы открыть файл.

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

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

Ответы [ 4 ]

49 голосов
/ 13 января 2012

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

fclose all

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

 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

Оператор delete вызывается автоматически 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
29 голосов
/ 13 января 2012

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

от справка:

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

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

По сути, вы даете ему дескриптор функции (в данном случае @()fclose(fid)), который он запускает, когда выходит из области видимости.

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

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

4 голосов
/ 24 сентября 2012

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

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

Эта альтернатива использует несколько уродливый 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 голос
/ 28 февраля 2014
fids=fopen('all');
fclose(fids);

% при условии, что вы хотите закрыть все открытые файловые дескрипторы

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...