【问题标题】:How can I close files that are left open after an error?如何关闭错误后保持打开状态的文件?
【发布时间】:2012-01-13 08:40:21
【问题描述】:

我正在使用

fid = fopen('fgfg.txt');

打开一个文件。

有时在我设法关闭文件之前会发生错误。在我关闭 Matlab 之前,我无法对该文件执行任何操作。

如果发生错误,如何关闭文件?

【问题讨论】:

标签: matlab file-io


【解决方案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!');

而且不需要关闭。

编辑: 我只是想包装fclose() 什么都不做。它可能对向后兼容很有用 - 对于使用文件 ID 的旧函数。

Edit(2): 继@AndrewJanke 好评后,我想通过在 fclose()

上抛出错误来改进删除方法
    function delete(this)          
        [msg,errorId] = fclose(this.fid);
        if errorId~=0
            throw(MException('safefopen:ErrorInIO',msg));
        end
    end

【讨论】:

  • +1 很好地使用了删除。一个问题:这是缓冲 I/O,因此失败的写入可能只显示在 fclose() 调用中;照原样,它们将在这里被默默地忽略。可以测试 fclose() 的返回值并在失败时调用 error()。在delete 中,错误将变成警告。可能希望包含一个可选的 fclose() 方法,以便调用者可以将错误公开为异常或处理它,如果 delete() 仍然打开,则关闭它。可能还想测试 fopen() 是否在构造函数中失败;照原样,它会存储一个无效的fid,然后在fwrite()或delete()中出错。
  • 我认为这是my idea ;) 无论如何,我会确保delete 方法是无异常的。唯一可能导致fclose 失败的是this.fid 是无效的文件句柄;在这种情况下,您不需要关闭文件。
  • @Nzbuu,我不会就原创性与您争论 :) 我喜欢它,所以我在您的帖子中添加了 (+1)。顺便说一句,fclose 可能会失败,因为在读/写期间发生了一些错误。查看 AndrewJankes 的评论。
  • MATLAB 的文件处理通常不会被缓冲:blogs.mathworks.com/loren/2006/04/19/high-performance-file-io。无论如何,当抛出异常时,您希望发生什么?文件句柄将消失,因为对象超出范围时必须删除。
  • @Nzbuu,关于你想要发生什么,我想把它留给我班的用户。但我认为了解发生的错误很重要,即使您选择在更高级别上忽略它们。
【解决方案2】:

您可以尝试 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 超出范围。

不需要try/catch 或条件代码。

【讨论】:

  • +1 onCleanup 很棒。但实际上,文件 I/O 涉及外部数据源的复杂任务之一。 Matlab 的文件句柄 I/O 大多使用状态码而不是抛出错误,因此每次调用 fopen()、fread()、fclose() 等都需要附带检查返回值或 ferror() 是否完全正确.这很烦人,所以没有人愿意在他们的代码中真正做到这一点。它非常适合包装它们并添加状态检查的自定义类,这些状态检查会在失败时调用 error()。
  • Andrew - 对于您经常执行的文件 io,我同意,但对于快速而肮脏的脚本,这是一种轻松升级游戏的方法。
【解决方案3】:

Andrey 的解决方案above 确实是解决这个问题的最佳方法。我只是想补充一点,如果您处理 safefopen 对象的数组,在方法 delete() 中抛出异常可能会有问题。在销毁此类数组期间,MATLAB 将对每个数组元素调用 delete(),如果有任何 delete() 抛出,那么您最终可能会得到剩余的打开文件句柄。如果您真的需要知道在销毁过程中是否出现问题,那么发出警告将是一个更好的选择恕我直言。

对于那些懒得将所有转发方法写入每个使用文件句柄的 MATLAB 内置函数的人,您可以考虑为类 safefopen 重载方法 subsref 的简单替代方案:

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!');

编辑: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 - 酷!我喜欢 feval 方法。我也同意您对文件数组的评论。
【解决方案4】:
fids=fopen('all');
fclose(fids);

%假设您要关闭所有打开的文件句柄

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-21
    • 2011-05-25
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多