web-dev-qa-db-ja.com

エラー後に開いたままになっているファイルを閉じるにはどうすればよいですか?

使ってます

fid = fopen('fgfg.txt');

ファイルを開きます。

ファイルを閉じる前にエラーが発生することがあります。 Matlabを閉じるまで、そのファイルでは何もできません。

エラーが発生した場合、どうすればファイルを閉じることができますか?

30
Lee

まず、コマンドを使用できます

_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
_
49

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_または条件付きコードは必要ありません。

29
Marc

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

開いているすべてのファイルハンドルを閉じたいと仮定します

1
Marcel