2012-01-13 20 views
30

Tôi đang sử dụngLàm cách nào để đóng các tệp được mở sau khi có lỗi?

fid = fopen('fgfg.txt'); 

để mở tệp.

Đôi khi xảy ra lỗi trước khi tôi quản lý để đóng tệp. Tôi không thể làm bất cứ điều gì với tập tin đó cho đến khi tôi đóng Matlab.

Tôi làm cách nào để đóng tệp nếu xảy ra lỗi?

+0

Liên quan chặt chẽ: [Làm thế nào để bạn xử lý tài nguyên trong MATLAB một cách ngoại lệ an toàn? (như “cố gắng… cuối cùng”)] (http://stackoverflow.com/q/1098149/52738) – gnovice

Trả lời

49

Trước hết, bạn có thể sử dụng lệnh

fclose all 

Thứ hai, bạn có thể sử dụng try-catch khối và đóng tập tin của bạn xử lý

try 
    f = fopen('myfile.txt','r') 
    % do something 
    fclose(f); 
catch me 
    fclose(f); 
    rethrow(me); 
end 

Có một cách tiếp cận thứ ba, tốt hơn nhiều. Matlab bây giờ là một ngôn ngữ hướng đối tượng với bộ thu gom rác. Bạn có thể định nghĩa một đối tượng bao bọc sẽ tự động xử lý vòng đời của nó.

Vì nó là có thể trong Matlab để gọi các phương thức đối tượng cả theo cách này:

myObj.method()

và theo cách đó:

phương pháp (myObj)

Bạn có thể xác định lớp bắt chước tất cả của lệnh tệp có liên quan và đóng gói vòng đời.

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 

Nhà điều hành xóa được gọi tự động bằng Matlab. (Có nhiều chức năng mà bạn sẽ cần phải bọc, (fread, fseek, vv ..)).

Vì vậy, bây giờ bạn có các chốt xử lý an toàn tự động đóng tệp cho dù bạn bị mất phạm vi hoặc lỗi xảy ra.

Sử dụng nó như thế này:

f = safefopen('myFile.txt','wt') 
fprintf(f,'Hello world!'); 

Và không cần phải đóng.

Chỉnh sửa: Tôi chỉ nghĩ đến việc gói fclose() để không làm gì cả. Nó có thể hữu ích cho khả năng tương thích ngược - cho các chức năng cũ sử dụng id tập tin.

Chỉnh sửa (2): Sau @AndrewJanke bình luận tốt, tôi muốn cải thiện các phương pháp xóa bằng cách ném các lỗi trên fclose()

function delete(this)   
     [msg,errorId] = fclose(this.fid); 
     if errorId~=0 
      throw(MException('safefopen:ErrorInIO',msg)); 
     end 
    end 
+5

+1 Sử dụng tuyệt vời xóa. Một bắt: đây là bộ đệm I/O, do đó việc ghi không thành công chỉ có thể hiển thị trong cuộc gọi fclose(); như vậy, chúng sẽ bị bỏ qua ở đây. Có thể kiểm tra giá trị trả về của fclose() và lỗi cuộc gọi() khi lỗi. Và trong 'xóa', lỗi sẽ chuyển sang cảnh báo. Có thể muốn bao gồm một phương thức fclose() tùy chọn để người gọi có thể phơi bày lỗi dưới dạng ngoại lệ hoặc xử lý lỗi và xóa() chỉ cần đóng nếu nó vẫn mở.Cũng có thể muốn kiểm tra fopen() cho thất bại trong hàm tạo; như là, nó sẽ stash một fid không hợp lệ, và sau đó lỗi trong fwrite() hoặc delete() khi hành động trên nó. –

+2

Tôi nghĩ rằng đây là [ý tưởng của tôi] (http://stackoverflow.com/a/9024064/21322);) Trong mọi trường hợp, tôi sẽ đảm bảo rằng phương pháp 'xóa' là không có ngoại lệ. Điều duy nhất có thể khiến 'fclose' bị lỗi là nếu' this.fid' là một tập tin không hợp lệ; trong trường hợp đó, bạn không cần đóng tệp. – Nzbuu

+1

@Nzbuu, tôi sẽ không tranh cãi với bạn về nguyên bản :) Tôi thích nó vì vậy tôi đặt (+1) tại bài đăng của bạn. Bằng cách này, fclose có thể thất bại do thực tế là một số lỗi xảy ra trong quá trình đọc/ghi. Xem bình luận của AndrewJankes. –

29

Bạn có thể thử một "chức năng" rất gọn gàng được thêm bởi ML được gọi là onCleanup. Loren Shure đã hoàn thành writeup trên nó khi nó được thêm vào.Đó là một lớp mà bạn khởi tạo với mã dọn dẹp của bạn, sau đó nó thực thi khi nó nằm ngoài phạm vi - tức là khi lỗi hoặc hàm kết thúc. Làm cho mã rất sạch sẽ. Đây là phiên bản chung của lớp mà Andrey đã có ở trên. (BTW, cho các nhiệm vụ phức tạp như đánh các nguồn dữ liệu bên ngoài, các lớp học tùy chỉnh chắc chắn con đường để đi.)

từ the help:

function fileOpenSafely(fileName) 
    fid = fopen(fileName, 'w'); 
    c = onCleanup(@()fclose(fid)); 

    functionThatMayError(fid); 
end % c executes fclose(fid) here 

Về cơ bản, bạn cung cấp cho nó một function handle (trong trường hợp này @()fclose(fid)) mà nó chạy khi nó nằm ngoài phạm vi.

Mã dọn dẹp của bạn được thực thi khi một lỗi được ném HOẶC khi nó thoát ra bình thường, vì bạn thoát khỏi fileOpenSafelyc nằm ngoài phạm vi.

Không try/catch hoặc mã có điều kiện cần thiết.

+1

+1 vì tôi không biết về onCleanup –

+3

+1 onCleanup thật tuyệt vời. Nhưng trên thực tế, tệp I/O * là * một trong những nhiệm vụ phức tạp đó là nhấn vào một nguồn dữ liệu ngoài. Xử lý tập tin của Matlab I/O chủ yếu sử dụng mã trạng thái thay vì ném lỗi, do đó, mọi lệnh gọi hàm fopen(), fread(), fclose() và cần kiểm tra giá trị trả về hoặc ferror() kèm theo . Và nó gây phiền nhiễu, vì vậy không ai bận tâm để thực sự làm điều đó trong mã của họ. Nó hoàn hảo cho một lớp tùy chỉnh kết thúc tốt đẹp chúng và thêm các kiểm tra trạng thái để chuyển sang các lỗi() các cuộc gọi không thành công. –

+1

Andrew - đối với tệp io mà bạn làm thường xuyên, tôi sẽ đồng ý, nhưng đối với tập lệnh nhanh và dơ bẩn, đây là cách để đẩy mạnh trò chơi của bạn một cách dễ dàng. – Marc

4

Andrey 's giải pháp above thực sự là cách tiếp cận tốt nhất cho vấn đề này. Tôi chỉ muốn thêm rằng ném một ngoại lệ trong phương pháp delete() có thể có vấn đề, nếu bạn đối phó với mảng của các đối tượng safefopen. Trong quá trình hủy bỏ một mảng như vậy, MATLAB sẽ gọi delete() trên mỗi phần tử mảng và, nếu có bất kỳ số phát nào là delete() thì bạn có thể kết thúc với các nút xử lý tệp còn mở. Nếu bạn thực sự cần phải biết liệu có gì sai trong quá trình hủy diệt thì việc đưa ra một cảnh báo sẽ là một lựa chọn tốt hơn IMHO.

Đối với những người cảm thấy lười biếng để viết tất cả các phương pháp chuyển tiếp đến từng được xây dựng trong MATLAB sử dụng xử lý tập tin, bạn có thể xem xét việc thay thế đơn giản của phương pháp subsref quá tải cho các lớp học 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 

Phương án này sử dụng hơi xấu xí feval, nhưng có lợi thế là làm việc ngay cả khi những kẻ MATLAB (hoặc chính bạn) quyết định thêm các chức năng mới liên quan đến xử lý tập tin, hoặc nếu số/thứ tự của các đối số đầu vào cho một thay đổi hàm đã cho. Nếu bạn quyết định đi cho subsref thay thế thì bạn nên sử dụng lớp safefopen như thế này:

myFile = safefopen('myfile.txt', 'w'); 
myFile.fprintf('Hello World!'); 

EDIT: Một nhược điểm của giải pháp subsref là nó không quan tâm đến tất cả các đối số đầu ra. Nếu bạn cần các đối số đầu ra sau đó bạn sẽ phải giới thiệu một số xấu xa hơn:

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 

Và sau đó bạn có thể làm những việc như:

myFile = safefopen('myfile.txt', 'w'); 
count = myFile.fprintf('Hello World!'); 
[filename,permission,machineformat,encoding] = myFile.fopen(); 
+1

+1 - Tuyệt! Tôi thích cách tiếp cận feval. Ngoài ra tôi đồng ý với bình luận của bạn về mảng các tập tin. –

1
fids=fopen('all'); 
fclose(fids); 

% giả định rằng bạn muốn đóng tất cả các mở filehandles

Các vấn đề liên quan