【发布时间】:2012-01-13 08:40:21
【问题描述】:
我正在使用
fid = fopen('fgfg.txt');
打开一个文件。
有时在我设法关闭文件之前会发生错误。在我关闭 Matlab 之前,我无法对该文件执行任何操作。
如果发生错误,如何关闭文件?
【问题讨论】:
我正在使用
fid = fopen('fgfg.txt');
打开一个文件。
有时在我设法关闭文件之前会发生错误。在我关闭 Matlab 之前,我无法对该文件执行任何操作。
如果发生错误,如何关闭文件?
【问题讨论】:
首先可以使用命令
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
【讨论】:
delete 中,错误将变成警告。可能希望包含一个可选的 fclose() 方法,以便调用者可以将错误公开为异常或处理它,如果 delete() 仍然打开,则关闭它。可能还想测试 fopen() 是否在构造函数中失败;照原样,它会存储一个无效的fid,然后在fwrite()或delete()中出错。
delete 方法是无异常的。唯一可能导致fclose 失败的是this.fid 是无效的文件句柄;在这种情况下,您不需要关闭文件。
您可以尝试 ML 添加的一个非常简洁的“功能”,称为onCleanup。 Loren Shure 在添加时有一个完整的writeup。这是一个你用你的清理代码实例化的类,然后当它超出范围时执行 - 即当它出错或函数结束时。使代码非常干净。这是Andrey 上面的类的通用版本。 (顺便说一句,对于像访问外部数据源这样的复杂任务,自定义类绝对是要走的路。)
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 或条件代码。
【讨论】:
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();
【讨论】:
fids=fopen('all');
fclose(fids);
%假设您要关闭所有打开的文件句柄
【讨论】: