檔案讀寫 方煒 台大生機系
本章重點 MATLAB 內建許多有關檔案讀寫的函數 對二進制檔案或 ASCII 檔案都可以進行處理 開啟 關閉 儲存
高階的檔案讀寫指令 先列出常用讀寫檔案指令,稍後將介紹用法 類別 支援檔案格式 指令 一般資料 MAT - MATLAB workspace load, save CSV - Comma separated numbers csvread, csvwrite DAT - Formatted text Importdata DLM - Delimited text dlmread, dlmwrite TAB - Tab separated text 試算表 XLS - Excel worksheet Xlsread WK1 - Lotus 123 worksheet wk1read, wk1write
高階的檔案讀寫指令(cont) 類別 支援檔案格式 指令 科學資料 CDF - Common Data FormatFITS - Flexible cdfread, cdfwrite Image Transport System Fitsread HDF - Hierarchical Data Format hdfread, hdfwrite 電影 AVI - Movie aviread 影像 TIFF, PNG, HDF, BMP, JPEG, GIF, PCX, XWD, CUR, ICO, RAS, PBM, PGM, PPM imread, imwrite 音訊 AU, SND - NeXT/Sun sound auread, auwrite WAV - Microsoft Wave sound wavread, wavwrite
csvread 使用 csvread 指令來讀取 條件: Ex:csvread01.m 資料檔案是由逗號分開 只有包含數值資料 fprintf('data.csv 的內容:\n'); type data.csv % 列出 data.csv 的內容 A = csvread('data.csv') % 將 data.csv 的內容讀到矩陣 A
result csvread 會傳回一個數值矩陣 其中缺席的資料將以 0 填入。 data.csv 的內容: 1, 2, 3 4, 5 6, 7, 8, 9 A = 1 2 3 0 4 5 0 0 6 7 8 9 csvread 會傳回一個數值矩陣 其中缺席的資料將以 0 填入。
dlmread 如果數值資料的分界符號(Delimiters)不是逗點,就不能使用 csvread 指令,而要改用 dlmread 指令 Ex:dlmread01.m fprintf('data.dlm 的內容:\n'); type data.dlm % 列出 data.dlm 的內容 A = dlmread('data.dlm', '\t') % 將 data.dlm 的內容讀到矩陣 A
Result 上例中data.dlm 的資料是以定位鍵(Tab)隔開,因此 dlmread 指令的第二個引數是 ‘\t’,以代表定位鍵 1 2 3 4 5 6 7 8 9 A = 1 2 3 0 4 5 0 0 6 7 8 9 上例中data.dlm 的資料是以定位鍵(Tab)隔開,因此 dlmread 指令的第二個引數是 ‘\t’,以代表定位鍵
textread 如果檔案資料包含數值及字串,我們就必須改用 textread 指令 Ex:textread01.m fprintf('data.txt 的內容:\n'); type data.txt % 列出 data.txt 的內容 [name, hobby, age] = textread('data.txt', '%s%s%d')
result 在上述範例中,data.txt 包含三個欄位(或是三直行的資料) textread 可在第二個引數指定資料型態 Timmy OnlineGames 13 Annie Chatrooms 10 Roger Tennis 41 name = 'Timmy‘ 'Annie' 'Roger' hobby = 'OnlineGames' 'Chatrooms' 'Tennis' age = 13 10 41 在上述範例中,data.txt 包含三個欄位(或是三直行的資料) textread 可在第二個引數指定資料型態 例如上例中 %s 代表字串,%d 代表整數 也同時將讀入的資料設定到不同的輸出引數 由於資料型態的不同,輸出引數也有不同的型態 以上述範例來說,name 和 hobby 都是字串異質陣列,而 age 則是數值陣列。
使用定位鍵來分隔欄位 上例中,我們利用空格來分隔欄位 如果欄位值本身也有空格 ? Ex:textread02.m 改用定位鍵來分隔欄位 fprintf('data2.txt 的內容:\n'); type data2.txt % 列出 data2.txt 的內容 [name, hobby, age] = textread('data2.txt', '%s%s%d', 'delimiter', '\t')
result 結果與前一例相同 textread 指令中加上對分界字元(Delimiters)的定義,就可以讀出由定位鍵所分隔的資料檔案 若不指定時,預設為空白鍵
讀取文字檔 textread 也可以讀取一個文字檔 同時把檔案內的每一列文字變成字串異質陣列裡面的每一個元素 Ex:textread03.m Contents = textread('textread03.m','%s','delimiter','\n','whitespace',''); class(contents) % 印出 contents 的資料類別 contents{1} % 列出 contents 第一列 contents{2} % 列出 contents 第二列
Result 上例使用 textread 讀入 textread03.m(也就是此範例檔案),並顯示此檔案的第一列和第二列。 ans = cell contents=textread('textread03.m','%s','delimiter','\n','whitespace',''); class(contents) % 印出 contents 的資料類別 上例使用 textread 讀入 textread03.m(也就是此範例檔案),並顯示此檔案的第一列和第二列。 textread 指令的用法還有很多,功能也很強大 在MATLAB下輸入「help textread」 可以得到完整的技術支援。
Fileread 若只是要將一個檔案的內容送到一個字串,可以使用 fileread 指令 Ex:範例16-6:fileread01.m out = fileread(‘data2.txt’); class(out) size(out)
Result ans = char 1 64 上例中,fileread 指令會將檔案 data2.txt 的內容送到字串變數 out 1 64 上例中,fileread 指令會將檔案 data2.txt 的內容送到字串變數 out class(out) 的值是 char ,顯示 out 的資料型態是字串 size(out) 顯示字串 out 的長度是 64。
更繁複的檔案格式 一般情況下 資料格式比較繁複時 盡量採取先前介紹的指令來進行讀寫。 需要一列一列讀進來 再進行剖析(Parsing) 相關指令將在以下介紹。
開啟檔案 無論讀寫 ASCII 或二進制檔案,都必需先用 fopen 函數來開啟檔案,語法如: fid = fopen(filename, permission) 其中 filename 表示欲讀寫的檔案名稱 permission 則表示欲對檔案進行的處理方式,可以是下列任一字串: ‘r’:只准讀取(reading)檔案 ‘w’:只准寫入(writing)檔案 ‘a’:只准加入(appending)檔案 'a+':可讀取及加入檔案(reading and appending) 省略第二個引數,permission 的預設值就是 ‘r’。
有關fopen 在windows下,permission 字串必需能夠分辨binary或 ASCII 檔案。 例如:若要讀binary檔案,則 permission 字串必需是“rb” fopen 另外支援很多 permission 字串,可輸入 「help fopen」 得到完整的資訊。 fopen 函數傳回一個檔案辨識碼,通常是個非負的整數,我們可用此辨識碼來對此檔案進行各種讀寫的處理。
Ex: fopen01.m 上例可知當檔案不存在時,回傳的 fid 是 –1 同時 message 會包含相關的錯誤資訊。 [fid, message] = fopen('no_such_file', 'r'); fprintf('fid = %d\n', fid); fprintf('message = %s\n', message); fid = -1 message = Sorry. No help in figuring out the problem . . . 上例可知當檔案不存在時,回傳的 fid 是 –1 同時 message 會包含相關的錯誤資訊。
Ex: fopen02.m 若開啟成功,則傳回的 fid 是一個大於 2 的整數,而且傳回的 message 是一個空字串,例如: [fid, message] = fopen('fopen02.m', 'r'); fprintf('fid = %d\n', fid); fprintf('message = %s\n', message); fid = 3 message =
Tips: fopen 可傳回第二個引數來顯示錯誤訊息。 為了安全起見,最好在每次使用 fopen 時,都測試其傳回的 fid 是否為有效值。 MATLAB 使用 fid=1 來代表「標準輸出(Standard Output) fid=2 代表「標準錯誤輸出」(Standard Error) 因此在使用這兩個 fid 的值時,可以不必使用 fopen 來開啟檔案。
關閉檔案 完成檔案的讀寫之後,可用 fclose 函數來關閉檔案,用法如下: 若一切順利,fclose 傳回 0。 status = fclose(fid); 若一切順利,fclose 傳回 0。 若無法順利關閉檔案,則 fclose 傳回 -1。 為避免因開啟檔案過多而造成系統資源浪費,一般在完成檔案的讀寫後,即應使用 fclose 來關閉檔案
Tips: 若要一次關閉所有開啟的檔案,可用 fclose('all') 或是 fclose all。 開啟及關閉檔案都是比較耗時的函數,因此盡量不要將他們置於迴圈之中,以提高程式執行效率。
讀取 ASCII 檔案 fgetl 函數: 可將 ASCII 檔案的內容中的某一列讀出 並將該列的 ASCII 內容以轉成字串傳回。 Ex:fgetl01.m fid = fopen('mean.m', 'r'); while feof(fid)==0 % feof 測試檔案指標是否已到達結束位置 line = fgetl(fid); disp(line); end
讀取 ASCII 檔案(cont) 執行上述程式後,MATLAB 會… fgets 和 fgetl 均可由檔案讀取一列資料: 先在目前目錄找尋 mean.m 若找不到,再根據搜尋路徑,找出 mean.m 指令的位置然後再將其內容一列一列地列出。 輸入「which mean」可顯示檔案所在的路徑 fgets 和 fgetl 均可由檔案讀取一列資料: fgetl 會捨去換行字元 fgets 函數則保留換行字元
應用:模擬unix的grep指令 grep :用來找出包含某一特定字串的一列 function grep(filename, pattern) fid = fopen(filename, 'r'); line_number = 0; while feof(fid) == 0, line = fgetl(fid); matched = findstr(line, pattern); if ~isempty (matched) fprintf('%d: %s \n', line_number,line); end line_number = line_number + 1; fclose(fid);
result 如欲列出 grep.m 中包含 'matched' 字串的每一列,可輸入如下: >> grep('grep.m', 'matched') matched = findstr(line, pattern); if ~isempty (matched)
fscanf 函數fscanf可對ascii檔案作更精確的讀取,用法如下: matrix = fscanf(fid, format) format 是格式指定字串(Format Specifier) 常用的格式指定字串有下列幾種: %s:字串 %d:10進位的整數 %g:雙倍精準(Double-precision)的浮點數(Floating-point Numbers) 其他各種格式指定字串可輸入 help fscanf 來得到詳細的說明
Ex: fscanf01.m 有一文字檔 test.txt 如下: 1 4 9 16 25 36 49 64 81 100 myData = 1 4 9 16 25 36 49 64 81 100 1 4 9 16 25 36 49 64 81 100 欲使用 fscanf 指令讀取其內容,可輸入如下: fid = fopen('test.txt', 'r'); myData = fscanf(fid, '%g'); fclose(fid); myData % 顯示 myData
Ex: sscanf sscanf 函數和 fscanf 的功能很類似 sscanf 函數從字串(strings)中讀取資料 ex:sscanf.m str = num2str([pi, sqrt(2), log10(3)]) %建立一字串str retrieved = sscanf(str, ‘%g’) %擷取str中的double str = 3.1416 1.4142 0.47712 retrieved = 3.1416 1.4142 0.4771
寫入 ASCII 檔案 fprintf 函數可將資料依格式指定字串來寫入 ASCII 檔案,其使用語法如下: fprintf(fid, format, y) 其中 fid 是欲寫入之檔案的辨識碼 format 是格式指定字串,用以指定資料寫至檔案的格式 y 是 MATLAB 的資料變數 常用的格式指定字串有下列幾種: %e:科學記號,即將數值表示成 a×10b 的形式 %f:固定欄寬(含整數與小數部份)的表示法 %g:自動選取 %e 或 %f 其他各種格式指定字串可輸入 help fprintf 來得到詳細的線上說明。
Ex: fprintf01.m 將平方根表寫入檔案 x = 1:10; y = [x; sqrt(x)]; fid = fopen('squareRootTable.txt', 'w'); fprintf(fid, 'Table of square root:\r\n'); fprintf(fid, '%2.0f => %10.6f\r\n', y); fclose(fid); dos('start squareRootTable.txt'); % 開啟 squareRootTable.txt
Result 在上例中… %2.0f 印出的總欄寬為 2,且不帶小數 %10.6f 印出的總欄寬為 10,包含 6位的小數 type squareRootTable.txt 來看結果
sprintf sprintf 函數和 fprintf 函數的功能很類似 sprintf 將資料以字串形式傳回 Ex: >> str = sprintf('log(%f) = %e\n', 2, log(2)) str = log(2.000000) = 6.931472e-001
暫存目錄 在某些應用上,可能需要用到暫存目錄及暫存檔案。 欲取用系統的暫存目錄,可用 tempdir如下: >> directory = tempdir Result: directory = C:\DOCUME~1\jang\LOCALS~1\Temp\
暫存檔案 欲開啟一暫存檔案,可用 tempname,如下: >> filename = tempname Result: C:\DOCUME~1\jang\LOCALS~1\Temp\tp273132
Tips 不同系統下tempdir 和 tempname會依作業系統的環境變數而產生不同的回傳字串 例如在 Windows 98 作業系統下 ’C:\windows\temp\’ tempname 傳回的字串可能是 ’C:\WINDOWS\TEMP\tp512124’。
應用:產生暫存的 HTML 檔案 以下利用 tempname 產生一個暫存的 HTML 檔案,然後再將此檔案顯示在瀏覽器。 Ex: writeHTML.m filename = [tempname, '.html']; fid = fopen(filename, 'w'); fprintf(fid, '<html><body>\n'); fprintf(fid, 'This is a test homepage written by MATLAB!\n'); fprintf(fid, '</body></html>'); fclose(fid); dos(['start ', filename]); % 啟動和 .html 相連結的應用程式
Result 上例產生之網頁如下: 讓MATLAB 的計算結果可用列表(Table)或多媒體檔案(如影像、聲音、動畫等)來呈現時,由網頁來顯示這些結果是不錯的選擇。
網路檔案的讀取 MATLAB 也可以直接讀取網路上的檔案 通常採用 URL(Universal Resource Locator)的方式來指定這些網路上的檔案: 一般網頁: http://www.mathworks.com FTP ftp://ftp.mathworks.com/pub/pentium/Moler_1.txt 本機硬碟檔案: file:///C:\winnt\matlab.ini
urlread Ex: urlread01.m contents = urlread('http://ecaaser3.ecaa.ntu.edu.tw/cea/cea1.htm'); disp(contents); 上例中使用 urlread 指令來讀取筆者在台大生機系的首頁 同時disp(); 將結果指定到字串變數 contents
urlwrite urlwrite可以直接在讀取網頁後,就儲存到本機硬碟中 Ex:urlread02.m tempFile = [tempname, '.html']; % 指定暫存檔案 urlwrite('http://www.google.com.tw', tempFile); % 將網頁內容寫到檔案 dos(['start ', tempFile]); % 開啟此檔案
Urlwrite(cont) 在上例中,我們將 www.google.com.tw 的網頁內容寫到一個暫存檔案 然後再使用瀏覽器開啟此檔案。 另一個和網路相關的功能 – sendmail 可用來寄發電子郵件 雖然這個功能和檔案讀寫沒有直接關係,但也在此一併說明。
sendmail Sendmail 指令的用法: sendmail(TO, SUBJECT, MESSAGE, ATTACHMENTS) 若是只有一位收件者,可用字串表示 若是有多位收件者,可以使用字串異質陣列來表示。 SUBJECT:主題,以字串表示。 MESSAGE:電子郵件的內容,以字串表示。 ATTACHMENTS:附加檔案,用異質陣列來表示。
Ex: sendmail01.m 請將 to 的內容改為你自己本身的電子郵件,並試著執行一次,以確認此程式碼的正確性。 to = {'email1@aaa.bbb.ccc', 'email2@aaa.bbb.ccc'}; subject = 'Test email from a MATLAB program'; message = 'This is a test email sent via sendmail.'; attachment = {'c:\windows\matlab.ini'}; sendmail(to, subject, message, attachment); 請將 to 的內容改為你自己本身的電子郵件,並試著執行一次,以確認此程式碼的正確性。
Tips MATLAB 6.5 的sendmail 不支援中文。 MATLAB 7.0 對 sendmail 新增了一些新功能 請輸入 help sendmail 來獲取最新的線上支援。 若要在郵件內容加入換列,可以使用 ASCII 碼「10」 例如:message=[’Sir:’, 10, ‘This is a test’]。
讀取二進制資料 用fread 函數可從檔案中讀取二進制資料 fread會將每一個位元組看成一個整數,並將結果以一矩陣傳回。 例如,檔案 test2.txt 的內容如下: This is a test!
Ex: fread01.m Result: This is a test! char 可將 myData 的整數轉成 ASCII 字元 fid = fopen('test2.txt', 'r'); myData = fread(fid); char(myData') % 驗證所讀入的資料是否正確 fclose(fid); Result: This is a test! char 可將 myData 的整數轉成 ASCII 字元 取 myData 的轉制是為了使印出的效果易於閱讀
Ex: fread02.m fread 函數可用第二個輸入引數來控制傳回矩陣的大小 fid = fopen('test2.txt', 'r'); myData = fread(fid, 4) % 只讀 4 個位元組 fclose(fid); myData = 84 104 105 115
Ex: fread03.m Result: 此時 myData 為 2X3 的矩陣。 p.s.上例中為長度4的向量 fid = fopen('test2.txt', 'r'); myData = fread(fid, [2 3]) fclose(fid); Result: myData = 84 105 32 104 115 105 此時 myData 為 2X3 的矩陣。 p.s.上例中為長度4的向量
Fread的第3個引數 控制 fread 在將二進制資料轉成 MATLAB 矩陣時所用的精確度 包含: 常用的精準度有下列幾種: 一次讀取的位元數(Number of Bits) 這些位元數所代表的資料型態 常用的精準度有下列幾種: char:帶符號的字元(8 bits) uchar:不帶符號的字元(8 bits) short:短整數(16 bits) int:整數(通常是 32 bits) long:長整數(32 或 64 bits) ushort:不帶符號的短整數(16 bits) uint:不帶符號的整數(32 bits) ulong:不帶符號的長整數(32 或 64 bits) float:單精準浮點數(32 bits) double:雙精準浮點數(64 bits)
Ex: fread04.m Result: myData = 26708 myData2 = fid = fopen('test2.txt', 'r'); myData = fread(fid, 1, 'short') fclose(fid); myData2 = bin2dec([dec2bin(abs('h'),8), dec2bin(abs('T'),8)]) Result: myData = 26708 myData2 =
說明: fread04.m 上例將 text2.txt 以短整數的方式讀入 要注意的是: 也就是以 16 bit 為一個單位來轉換成整數 讀出來的第一個數字,會等於將 ‘h’ 和 ‘T’ 轉換成 8 bit,再並排成 16 bits 然後再轉換成十進位的整數值 要注意的是: 雖然原檔案的前兩個位元組是 ‘T’ 和 ‘h’,但是在以 16 bits 為一個單位時… ‘T’ 會是 LSB(Least Significant Bits) 而 ‘h’ 則是 MSB(Most Significant Bits) 因此我們再重組成 16 bits 時,‘h’ 會在前而 ‘T’ 會在後。
寫入二進制檔案 fwrite 函數: 以下用fwrite 產生一大小為 40 bytes 的二進制檔案 矩陣 -> 二進制格式 -> 寫入檔案 傳回成功寫入的個數。 以下用fwrite 產生一大小為 40 bytes 的二進制檔案 在使用 type 指令來顯示檔案內容時,看不到其內容 因為是 binary 檔案 如果 count 的值小於 10,就表示 fread 的運算有誤 Ex: fwrite01.m fid = fopen('test.bin', 'w'); count = fwrite(fid, randperm(10), 'int32'); fclose(fid); type test.bin
控制檔案位置指標 檔案位置指標: 決定下一次進行資料讀取或寫入的位置。 控制此指標的函數如下: feof 測試指標是否在檔案結束位置 fseek 設定指標位置 ftell 取得指標位置 frewind 重設指標至檔案起始位置
foef 測試指標是否在檔案結束位置 Ex:feof01.m 在上例中,feof(fid) 傳回 1,表示檔案指標已經在檔案結束位置 fid = fopen('test.txt'); A = fscanf(fid, '%g', [3 4]) feof(fid) 在上例中,feof(fid) 傳回 1,表示檔案指標已經在檔案結束位置 因為 test.txt 只包含 10 個數目字。
result A = 1 16 49 100 4 25 64 0 9 36 81 0 ans = 1
frewind 從 test.txt 的起始位置讀出資料 >> frewind(fid); >> A = fscanf(fid, '%g', 5) Result: A = 1 4 9 16 25
fseek 用於設定指標位置,其格式如下 status = fseek(fid, offset, origin) 其中 fid 是檔案識別碼 offset 是偏移量(以 byte 為單位,可以是正數或負數) origin 代表基準點,可包含下列字串: 'cof':指標的現在位置(Corrent Position in File) 'bof':檔案的起始位置(Beginning of File) 'eof':檔案的結束位置(End of File)
ftell 傳回現在的指標位置 Ex:ftell01.m 40 代表指標已在檔案結束位置。 fid = fopen('test.bin', 'w'); count = fwrite(fid, randperm(10), 'int32'); ftell(fid) ans = 40 40 代表指標已在檔案結束位置。
再移動一次指標位置 欲將指標向前移動 10 bytes,可輸入如下: >> fseek(fid, -10, 'cof'); >> ftell(fid) Result: ans = 30 此代表指標的新位置是 30。
指令彙整 高階的文字檔案讀寫 開檔/關檔 Csvread/csvwrite Dlmreadd/lmwrite Textread Fopen 讀寫以逗點分界欄位的數值資料檔案 Dlmreadd/lmwrite 讀寫以特定字元來當分界欄位的數值資料檔案 Textread 讀入固定欄位的文字資料檔 開檔/關檔 Fopen 開啟檔案 Fclose 關閉檔案
指令彙整(cont) 格式化資料 二進制資料 Fscanf Fread Fprintf Fwrite Fgetl Fgets 從檔案讀取格式化資料 Fprintf 將格式化資料寫入檔案 Fgetl 從檔案讀取一列資料,但捨去換行字元 Fgets 從檔案讀取一列資料,但保留換行字元 二進制資料 Fread 從檔案讀取二進制資料 Fwrite 將二進制資料寫入檔案
指令彙整(cont) 字串 檔案位置控制 Sprintf Sscanf Ftell Frewind Ferror 將格式化資料寫至字串 從字串讀取格式化資料 檔案位置控制 Ferror 檔案輸入/輸出的錯誤狀態 Feof 測試是否已到檔案結束位置 Fseek 設定檔案定位器 Ftell 讀取檔案定位器 Frewind 回轉檔案定位器
指令彙整(cont) 暫存檔案/目錄 Tempdir 取得暫存目錄的名字 Tempname 取得暫存檔案的名字