Perl小技巧:檔案操作

來源:互聯網
上載者:User
  • 翻 譯:SaladJonk
  • 審 校:qiang
  • 出 處:中國Perl協會 FPC(Foundation of Perlchina)
  • 原 名:Perl circus:file operation
  • 作 者:Luke Melia
  • 原 文:
  • 發 表:2002

Perlchina提醒您:請保護作者的著作權,維護作者勞動的結晶。

目錄

  • 1
    Find : 找到具有指定特徵的檔案
  • 2
    搜:對目錄進行遞迴搜尋
  • 3
    讀 :一次讀入整個檔案內容
  • 4
    賦值 : 把一個檔案控制代碼賦給另一個檔案控制代碼
  • 5
    寫:同時向兩個檔案控制代碼執行寫操作
  • 6
    More: 從一個檔案的完全路徑中找出它的名字
  • 7
    改:改變檔案的所有者

 


Find : 找到具有指定特徵的檔案
$path = "/path/to/dir/";
opendir DIR, $path;
@arr1 = readdir DIR;
@arr2 = grep{-T "$path$_"} @arr1; #text files only
@arr3 = grep{!-d "$path$_"} @arr1; #no directories
@arr4 = grep{-s "$path$_" < 1024} @arr1; #less than 1K

代碼解釋:假如被測試的目錄項是一個文字檔,那麼 -T
檔案操作符就會返回真。其實針對目錄項的測試操作還有很多。(註:檔案和目錄在系統中都是以目錄項的形式來管理的,所以要區別一個目錄項指向的是一個檔案
還是一個目錄需要相應的操作符)。注意上面的 readdir 函數返回指定目錄下的所有目錄項。因為在 grep
函數中對目錄項的測試需要檔案的完全路徑,所以我們把 $PATH(儲存了目錄項的部分路徑) 和
$_(儲存了目錄項的名字)中的內容聯合起來得到檔案的完全路徑


搜:對目錄進行遞迴搜尋
use File::Find;

find(/&handleFind, 'imac:documents:code');

sub handleFind{
my $foundFile = $File::Find::name;
print "$foundFile/n" if ($foundFile =~ //.html?$/i);
}

運行結果: imac:documents:code:index.html imac:documents:code:perl:example.HTM

代碼討論:那些工作於 Unix 系統的 Perl 程式員可以非常簡便的利用 UNIX
上提供的工具來完成許多日常的工作,比如遞迴的列出指定目錄下的所有目錄項(也就是列出指定目錄及指定目錄子目錄下的所有目錄項目)。然而 Perl
的一個最大的特徵就是可以運行於很多的平台上。所以如果你碰巧工作在一個非 UNIX 的平台,或者如果你雖工作在 UNIX
平台,但不喜歡使用系統工具寫指令碼,你可以選擇 Perl。要完成這些巧妙的工作,你需要使用 perl 中的 File:Find
模組。當你載入了這個模組的時候,你就可以使用其中的 find
子函數,在調用這個函數的時候,需要帶參數:第一個參數是一個函數的引用,這個函數由你自己建立,每次一個檔案被找到的時候,它都會運行。接下來的一個參
數是一串你想要搜尋的路徑。我寫的這個樣本指令碼是運行在 Macintosh OS 8.x 系統上的,所以我使用了 Mac 系統的路徑分隔字元
:。如果是在 Windows,你可以用反斜線,如果是在 Unix 系統則是正斜杠(至於在 Amiga
系統上用什麼我就不知道了)。總之,find 函數將會在每次找到一個檔案的時候調用你給出的子函數,而且會對子目錄進行尋找。在我的
handledfind 子函數中,我通過這個模組特定變數 $File::Find::name 來獲得每次 find
找到的檔案名稱。然後,就可以對該檔案執行任何你想的測試,在上面的例子中,我們輸出有 .html 的副檔名檔案名稱。


讀 :一次讀入整個檔案內容
open FH, "< anthem";
$/ = undef;
$slurp = <FH>;
print $slurp;

運行結果:一下就顯示了所有的檔案內容,此刻你應該非常的自豪。:) 代碼討論:角括弧 <>
對檔案控制代碼進行操作,在標量上下文中它將返迴文件的下一條記錄,在數組上下文中它將返回所有的記錄。在預設的情況下,檔案中的記錄被認為是由分行符號分開
(例如斷行符號或其他代表新行開始的字元)。你可以重新設定這個預設的分隔字元,然後 Perl 將會以你指定的分隔字元為準來替代分行符號。全域變數 $/
裡儲存了輸入檔案的分隔字元,如果你把 $/ 的值設定為 undef ,那麼 Perl
將會認為整個檔案是一條記錄(因為此刻已經沒有檔案分隔字元了)。牢記 $/
是全域變數,千萬不要在指令碼的其他地方不經意的改變它,這個錯誤將很難被發現。你可能會問,我們能否不改變
$/,而採用把檔案的所有記錄讀到一個數組中,然後把數組聯合成一個很長的字串(比如 $slurp =
join("",<FH>);)的方法實現一次讀入檔案。當然這也是一個有效解決辦法,但是你會發現它很慢,是否選用它取決你的應用,取決
於你是否關心運行速度。


賦值 : 把一個檔案控制代碼賦給另一個檔案控制代碼
open(MYOUT, "> bottle.txt");
*STDOUT = *MYOUT;
print "message";

運行結果:文字檔 bottle.txt 現在包含 message 字串。代碼討論:以前可能你配合使用過 Print
函數和檔案控制代碼,但是你是否知道就算你沒有使用檔案控制代碼,Perl 也預設你在使用一個稱為 STDOUT 的控制代碼?C 程式員知道 STDOUT
代表標準輸出,也就是通常的螢幕,或終端視窗(或者是 CGI 程式的輸出端 -
瀏覽器)。在這裡我們完成的工作是建立我們自己的檔案控制代碼,它指向一個給定的檔案,然後我們做了一件比較鬼的工作,使用 * 首碼把 STDOUT
轉換為 typeglob 類型。Typeglob 類型的資料可以有別名,這樣一個變數可能會指向另一個其他名字的變數。上面第二行代碼使
STDOUT 指向 MYOUT 變數。所以執行 print 操作時的預設輸出對象也就成為了我們建立的檔案控制代碼。


寫:同時向兩個檔案控制代碼執行寫操作
use IO::Tee; 
$tee = IO::Tee->new(">> debuglog.txt", /*STDOUT);
print $tee "an error ocurred on ".scalar(localtime)."/n";

運行結果:an error ocurred on Fri Feb 23 21:44:20 2001
代碼討論:如果,由於種種原因你想要同時向兩個位置寫入同一個字串,這和 UNIX 下的 tee 工具的用途一樣。即使你不是工作在 Unix
平台上,Perl 也通過 Tee 模組為你提供這個功能。Tee 模組可以在 CPAN 下載,你應該把它安裝到 Perl 的 IO
庫檔案夾中。Tee 模組以 OOP 方式編寫,所以使用它之前你應該首先使用它的 new 方法來建立一個 Tee
對象,整個過程需要兩個參數,每個參數既可以是代表檔案控制代碼的字串,也可以是一個對已開啟的檔案控制代碼的引用。在上面的例子中,我們用一個字串來代表一
個以附加模式開啟的檔案控制代碼,它指向名為 debuglog.txt 的檔案,另一個參數是系統內建的檔案控制代碼
STDOUT,整個控制代碼是系統自動建立的,print 函數預設情況對它進行操作。為了得到一個檔案控制代碼的引用我們需要對一個 typeglob
類型的資料使用反斜線。Typeglob 可以代表任何已命名的某個變數,不論它是數組,散列還是標量等。使用 *
很有必要,因為檔案控制代碼自己沒有首碼符號。new 操作符返回 Tee 類的一個執行個體對象,然後我們把整個執行個體賦給 $tee
標量。現在,無論什麼時候我們向 $tee 進行寫入操作,我們都同時向兩個位置進行寫操作。


More: 從一個檔案的完全路徑中找出它的名字
use File::Basename;
$path = "/docs/sitecircus.com/html/tricks/trick.of.the.week.html";
$basename = basename($path, ".html");
print $basename;

運行結果:trick.of.the.week
代碼討論:好了,成功了。問題是要找出檔案的名字,要不帶任何路徑首碼,不帶任何副檔名。File::Basename
模組可以使這很容易實現,我們只需要把檔案的完全路徑還有要剔除的副檔名傳給它。上面的 path 變數是檔案的完全路徑,注意檔案分隔字元是
/,這個字元很特殊,因為它是作業系統的保留字元。這裡你不能在檔案名稱裡使用系統的分隔字元。你應該知道當今流行的作業系統都使用自己獨特的檔案分隔
符:Unix使用 /,Windows 使用 /,Macintosh 使用 :(順便說一下,在 Windows 上的 Perl
指令碼中,你既可以使用 /也可以使用 /作為檔案分隔字元,Perl
的解譯器能理解你的意思)。File::Basename,當然,能正確在完全路徑中找到檔案名稱,不論時在什麼系統下。


改:改變檔案的所有者
($uid, $gid) = (getpwnam($username))[2,3]
or die "$user not in passwd file";
chown ($uid, $gid, $file)
or warn "couldn't chown $file.";

運行結果:無輸出代碼討論:有的時候,你可能知道一個使用者名稱,而你想用這個使用者名稱做些事,比如改變一個檔案的所有者。但是不幸的是,Perl 的
chown 命令不能接受使用者名稱作為參數,但是可以接受一對數字:userid 和 groupid。雖然有這些不便之處,Perl
並沒有讓我們陷入困境,我們可以把使用者名稱作為 getpwnam 函數的參數,獲得一個數組,裡麵包含了使用者名稱對應的 userid 和
groupid,分別對應著數組裡的第二和第三個元素。

作者:Luke Melia

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.