使ってます
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
_
3番目のアプローチがあります。これははるかに優れています。 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()でエラーをスローすることにより、deleteメソッドを改善したいと思います。
_ function delete(this)
[msg,errorId] = fclose(this.fid);
if errorId~=0
throw(MException('safefopen:ErrorInIO',msg));
end
end
_
onCleanup
と呼ばれるMLによって追加された非常にきちんとした「関数」を試すことができます。 Loren Shureは、追加されたときに完全な writeup を持っていました。これは、クリーンアップコードを使用してインスタンス化するクラスであり、スコープ外になると実行されます。つまり、エラーが発生したとき、または関数が終了したときです。コードを非常にクリーンにします。これは、 Andrey が上記で持っていたクラスの汎用バージョンです。 (ところで、外部データソースへのアクセスなどの複雑なタスクの場合、カスタムクラスが最適です。)
から ヘルプ:
_function fileOpenSafely(fileName)
fid = fopen(fileName, 'w');
c = onCleanup(@()fclose(fid));
functionThatMayError(fid);
end % c executes fclose(fid) here
_
基本的に、スコープ外になったときに実行される 関数ハンドル (この場合は@()fclose(fid)
)を指定します。
クリーンアップコードは、エラーがスローされた場合OR正常に終了した場合、fileOpenSafely
を終了し、c
がスコープ外になるため)実行されます。
_try/catch
_または条件付きコードは必要ありません。
Andrey の解決策 上記 は確かにこの問題への最良のアプローチです。 safefopen
オブジェクトの配列を処理する場合、メソッドdelete()
で例外をスローすると問題が発生する可能性があることを追加したかっただけです。このような配列の破棄中に、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();
fids=fopen('all');
fclose(fids);
開いているすべてのファイルハンドルを閉じたいと仮定します