[PERL] 20- 檔案目錄處理

URL Link //n.sfs.tw/12103

2018-01-04 21:45:15 By 張○○

對於檔案讀寫,PERL很容易就上手,我常用來同時處理上千個檔案,都在極短的時間內就能處理完。但PERL對檔案的支援內容很多,我粗分為幾個部分:

讀取檔案

寫入或添加內容

刪除

讀取二進位檔

更名

目錄操作

檔案測試

 

讀取檔案

無論是讀取或寫入,檔案處理的基本步驟就是

開啟檔案
讀取內容或寫入資料
關閉檔案

先看範例,並分別說明如下:

open(FILE, 'file.txt') or die "$!";
while( defined( $line = <FILE> )){
  print $line;
}
close(FILE);

開啟檔案

open(FILE, '\path\to\file.txt');

開啟使用open指令,第一個項目放入file handle,是一種檔案指標,名稱隨意,習慣上會用大寫來表示,不必加引號,就算有加也沒關係;

第二個項目放檔案的(路徑+)名稱。

如果開啟失敗,例如檔案不存在或權限錯誤,就會回傳 undef ,後面的操作就會失敗,所以要改成這樣:

open(FILE, '\path\to\file.txt') or die "$!";

這行

open(FILE, '\path\to\file.txt') or die "$!";

分成兩個部分,青底的如果不為真(undef)就會處理綠底的這部分,其中 die 會傾印後面的 "$!"並結束。

$!是PERL的特殊變數,意思是「錯誤訊息」。

這種"or die" 的寫法常會在PERL中看到,可說是正字標記。整句可看成:「如果無法開檔就印出錯誤訊息並結束。」

讀取內容

FILE是我設定的檔案處理指標,在取用時得用角刮號 '<>' 給包起來。

$line = <FILE>

一次取一行,並將處理指標指到此行的下一個字元上,再執行一次時會取下一行,如果取不到資料,會回傳 undef

當我用 while 時就能取回所有的資料直到取不到為止。

while( defined( $line = <FILE> )){ }

關閉檔案

close(FILE);

除非檔案處理指標重覆使用,可以省略這個步驟外,每次都記得關閉檔案。

 

寫入或添加內容

開啟檔案的open指令第二個項目前面如果加上一個符號 '>' 就代表「開啟以供寫入檔案」;加上符號 '>>' 就代表「開啟以供為入內容到檔案後端」。

如果要寫入的檔案不存在自動建立新檔,差別在於寫入時如果原檔案有內容直接會被清空;而添加的會加檔案的後面。

寫入

open(FILE, '> \path\to\new.txt') or die "$!";

添加

open(FILE, '>> \path\to\append.txt') or die "$!";

寫入指定編碼 utf8,這兩者都可以

open(FILE, '>:encoding(utf8)', '\path\to\utf8.txt') or die "$!";

寫入內容的方式是使用印的

print FILE "要寫入的內容第一行\n";
print FILE "要寫入的內容第二行\n";

也可以一次把陣列印進去

@line = ("line1", "line2");
print FILE @line;

把陣列一次印進去的方法在自己覆寫自己時很方便,但限制是檔案並不大的情況。例如以下範例,要把檔案 'file.html' 中的所有字串 'default.html' 改成 'index.php' 並儲存。

# 讀原檔
open(FILE, 'file.html') or die "$!";
@origin = <FILE>;
close(FILE);

# 寫入原檔
open(FILE, '> file.html') or die "$!";
foreach ( @origin ){
  $_ =~ s/default\.html/index.php/g;
}
print FILE @origin;
close(FILE);

第9行是要處理的部分,可依需求作修改,這樣子PERL 就能替我們作很多事,尤其是大量取代。

 

刪除檔案

使用 unlink 指令即可

unlink('file.html') or die "$!";

 

讀取二進位檔

前面介紹的續取寫入方式如果未指定都是以純文字來處理,如果要用二進位方式來處理檔案,會加上一指令 binmode

來看範例讀取圖檔,這個範例把圖檔 pic.jpg 的每一個位元用十進位給印出來。

open(FILE, 'pic.jpg') or die "$!";
binmode(FILE);
while( defined( $ch= getc(FILE))){
  print ord($ch). " ";
}
close(FILE);

說明

第2行 binmode改為二進位模式
第3行 使用getc一個個讀取字元
第4行 使用ord把字元由ASCII表轉成10進位,這行也可以這樣改寫成十六進位表示:

    printf("%02X ", ord($ch));

結果

137 80 78 71 13 10 26 10 0 0 0 13 73 72 68 82 0 0 0 150 0 0 0 150 8 6 0 0 0 60 1 113 226 0 0 0 4 103 65 77 65 0 0 177 142 124 251 81 147 0 0 0 32 99 72 82 77 0 0 135 15 0 0 140 15 0 0 253 82 0 0 129 64 0 0 125 121 0 0 233 139 0 0 60 229 0 0 25 204 115 60 133 119 0 0 0 6 98 75 71 68 0 255 0 255 0 255 160 189 167 147 0 0 0 9 111 70 70 115 0 0 0 0 0 0 0 54 0 83 54 39 187 0 0 0 9 112 72 89 115 0 0 14 196 0 0 14 196 1 149 43 14 27 0 0 0 7 116 73 77 69 7 224 10 22 10 22 49 165 131 145 251 0 0 0 9 118 112 65 103 0 0 0 150 0 0 1 3 0 244 2 113 163 0 0 68 133 73 68 65 84 120 218 237 189 119 152 28 213 149 254 255 185 85 213 185 123 114 206 154 81 206 9 161 128 16 65 128 200 24 227 0 182 193 54 11 235 200 218 94 123 215 203 207 235 93 127 189 193 187 235 245 174 215 1 99 147 131 13 38 8 147 17 81 72 66...以下略

 

更名

更名使用rename函數

rename('pic.jpg','picnew.jpg') or die "$!";

目錄也可以更名

rename('dir','dirnew') or die "$!";

 

目錄操作

和檔案操作一樣,目錄操作有以下的函數:

opendir 開啟目錄

readdir 讀取目錄內容

closedir 關閉目錄

rmdir 刪除目錄

mkdir 建立目錄

印出目錄的內容

直接由範例來解說,要印出指定目錄的內容

opendir( DIR, './') or die "$!";

while( defined( $onedir= readdir(DIR))){
  print $onedir. "\n";
}
closedir( DIR);

說明

第1行 開啟指定此檔所在的目錄
第3行 readdir是讀取目錄中項目的方法

結果

.
..
.bash_logout
.bash_profile
.bashrc
i.php
.mysql_history
.bash_history
file.txt
.viminfo
01.pl
_backup
.ssh
uca.cer
02.pl
.pki

由上面範例得知,無論是不是隱藏檔都會被印出來,並且照著某種神秘的順序印出?你也可以用下面這個方法挑出你想要的檔案類型:

opendir(DIR, $path);
@files = grep { /\.pl$/i } readdir(DIR);
closedir(DIR);

 

建立目錄

如果目錄不存在,則建立目錄

mkdir("path/to/dir",0755) unless -d "path/to/dir";

目錄建立一次只能建一層,例如上例中,你的目錄 path/to/ 要先存在

第二個參數0755是目錄的權限,千萬不能寫成755或是加了引號 "0755",結果會出乎意料。

 

刪除目錄

rmdir path/to/dir
或加上括號
rmdir(path/to/dir);

rmdir 在PERL裡面非常的雞肋,因為只要目錄不是空的,刪除目錄就失敗並報錯:

   Directory not empty

所以他的可用性很低,你可以使用use File::Path 中的rmtree來解決這個問題:

use File::Path;
rmtree path/to/dir or die $!;

前面讀取目錄清單的缺點是不知道哪個是檔案?哪個是目錄?因此要進行「檔案測試」。

 

檔案測試

使用簡單的檔案測試運算子就能進行檔案的測試,例如分出檔案、目錄、建立日期、大小、權限等。他的寫法是這樣

if( 檔案測試運算子 目錄名){ .. }

例如:

if(-e 'somefile.txt'){ print "檔案已存在"; }
if(!-e 'somefile.txt'){ print "檔案不存在"; }
if(-z 'somefile.txt'){ print "檔案大小為0,檔案不存在為假"; }
if(-f 'somefile.txt'){ print "這是一個檔案"; }
if(-d 'somename'){ print "這是一個目錄"; }
print -s '01.pl';  # 檔案大小,無此檔回傳 undef
if(-M 'somefile' > 7){ print "建立日期大於7天"; }

可使用的檔案測試運算子,請參考官網[1]

上面的例子常常被寫成後置表示法,再搭配unless的使用,這也是PERL的特色,例如檢測是否可讀的權限

print "可讀取" if -r 'somefile';

print "不可寫入" unless -w 'somefile';

 

下一篇談到資料庫的運用。

上一篇 19-模組的安裝和維護
回到目錄 01-撰寫第一隻PERL程式
下一篇 21- 使用資料庫

參考資料

[1] https://perldoc.perl.org/functions/-X.html

[2] https://caveofprogramming.com/perl-tutorial/perl-file-delete-deleting-files-and-directories-in-perl.html