Подтвердить что ты не робот

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

Я использую

fid = fopen('fgfg.txt');

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

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

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

4b9b3361

Ответ 1

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

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(), чтобы ничего не делать. Это может быть полезно для обратной совместимости - для старых функций, которые используют идентификаторы файлов.

Изменить (2): Следуя @AndrewJanke хорошему комментарию, я хотел бы улучшить метод удаления, бросив ошибки на fclose()

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

Ответ 2

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

из Справка:

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 или условный код.

Ответ 3

Andrey решение выше действительно лучший подход к этой проблеме. Я просто хотел добавить, что выбрасывание исключения в методе 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

Этот вариант использует несколько уродливый 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();

Ответ 4

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

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