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

31

Я использую

fid = fopen('fgfg.txt');

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

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

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

Теги:
file-io

4 ответа

49
Лучший ответ

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

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
  • 5
    +1 Отличное использование удаления. Один улов: это буферизованный ввод / вывод, поэтому неудачные записи могут отображаться только в вызове fclose (); как они здесь будут молчаливо игнорироваться. Может проверить возвращаемое значение fclose () и вызвать error () при ошибке. И при delete ошибка превратится в предупреждение. Может потребоваться включить необязательный метод fclose (), чтобы вызывающая сторона могла выставлять ошибку как исключение или обрабатывать ее, а delete () просто закрывать, если она все еще открыта. Может также потребоваться проверить fopen () на наличие ошибок в конструкторе; как есть, он сохранит неверный fid, а затем при работе с ним произойдет ошибка в fwrite () или delete ().
  • 2
    Я думаю, что это была моя идея ;) В любом случае, я бы гарантировал, что метод delete содержит исключений. Единственное, что может привести к сбою fclose - это если this.fid является недопустимым дескриптором файла; в этом случае вам не нужно закрывать файл.
Показать ещё 4 комментария
30

Вы можете попробовать очень аккуратную "функцию", добавленную 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 или условный код.

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

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();
  • 1
    +1 - Круто! Мне понравился февальный подход. Также я согласен с вашим комментарием по поводу массивов файлов.
1
fids=fopen('all');
fclose(fids);

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню