1. 程式人生 > >Perl語言入門:檔案操作和異常處理初步

Perl語言入門:檔案操作和異常處理初步

Perl使用一種叫“檔案控制代碼”的變數來操作檔案

在文章末尾將補充講解Perl5新加入的語法特性

檔案控制代碼

檔案控制代碼(filehandle)是檔案操作中用來存放檔案唯一識別符號的名稱空間

或者說,檔案控制代碼是一個I/O連線的名稱

在檔案I/O中,要從一個檔案讀取資料,應用程式要呼叫作業系統函式並傳入檔名,然後選取一個到該檔案的路徑來開啟檔案;用來溝通檔案和I/O原始資料的就是檔案控制代碼

Perl內建六種保留檔案控制代碼 STDIN STDOUT STDERR DATA ARGV ARGVOUT
對應,標準輸入、標準輸出、標準錯誤、從執行它的指令碼獲取輸入、命令列引數、命令列引數輸出

檔案控制代碼是檔案操作的基礎,可以作為引數傳給「開啟檔案函式」

檔案控制代碼通常大寫

open函式

open CONFIG, '讀file.txt';

其中,“CONFIG”就是程式設計師指定的檔案控制代碼,該句開啟檔案控制代碼OCNFIG,並從file.txt中讀入資料(預設情況下就是“讀”,所以 ‘<’符號是可省略的)

幾種open函式的形式:

open HANDLE1, '<a.txt'; #讀入資料
open HANDLE2, '>b.txt'; #寫入資料
open HANDLE3, '>>c.txt';    #追加資料

關閉檔案(控制代碼)

所謂“關閉檔案控制代碼”,就是改告訴作業系統“程式對檔案的資料流操作已經完成,請系統將尚未寫入的輸出資料寫入磁碟以免有人等著使用”

假設你熟悉I/O系統,應該知道這裡還有一些細節:一般來說,當檔案控制代碼關閉時,如果檔案中仍有輸入它會被忽略;如果管道中仍有輸入,那麼對這個管道進行讀寫操作的程式會收到管道被關閉的訊號;如果有檔案或管道的輸出,那麼緩衝區會被重新整理(也就是儲存在緩衝區的內容會馬上傳送出去);如果檔案控制代碼加了鎖,那麼該鎖會被釋放……

當重新開啟檔案控制代碼時,Perl會自動關閉原先的檔案控制代碼。在程式結束時Perl也會自動關閉所有的檔案控制代碼

正因為Perl是這樣貼心,以至於我們根本不需要操作檔案控制代碼的關閉問題;但是如果要程式碼寫的好看些,最好在適當的時候加上close函式

die函式

Perl使用die函式來捕獲異常

可以通過操作的狀態返回碼來避免異常:

my $success = open LOG, '>>', 'logfile';
if(! $success){
    #open操作失敗
    ...
}

現在可以使用die函式來捕獲異常(規範寫法):

if(! open LOG, '>>', 'logfile'){
    die "Cannot create logfile: $!";
} 

如果open失敗,die會終止程式的執行,並且告訴我們無法建立日誌檔案
冒號後面的$!代表(儲存著)“可讀的系統錯誤資訊”,如permission deniedfile not found之類的語句
另外die函式還會打印出出現問題的程式所在行數

Cannot create logfile: permission denied at your_program line 1234

如果不想輸出行數資訊,可以在die函式的末尾加上\n換行符

if(@ARGV<2){
    die "Not enough arguments\n";
}

warn函式

warm函式的功能和die函式的功能差不多

不同的是,warm函式不會終止程式的執行

使用檔案控制代碼來操作檔案

讓我們回到檔案操作的話題

if(! open PASSWD, "/etc/passwd"){
    die "How did you get logged in? ($!)";
}
while(<PASSWD>){
    chomp;
    ...
}

在這個栗子中可以看到,所謂的行輸入操作符<>不過是括住輸出資訊(檔案控制代碼)的括號而已

#函式入操作符的使用:<HANDLE>

“行輸入操作符”也叫“鑽石操作符”(長得像~)

鑽石操作符用來從終端命令列讀取引數,並解析檔案控制代碼用於檔案操作

使用檔案控制代碼(重定向輸出)
print LOG "Hello world\n";  #輸出到檔案控制代碼LOG
print STDERR "World hello\n";
改變預設的檔案輸出控制代碼

預設情況下,檔案輸出控制代碼是STDOUT

Perl使用select來改變預設的檔案輸出控制代碼

select函式的作用域,除非顯式重新改回原來STDOUT否則將一直延續到程式結束

select MYHANDLE;
print "What are you doing?\n"
print "I am typing with Perl...\n";

現在預設情況下這兩句話都被輸出到檔案控制代碼MYHANDLE中去了..

特殊變數$|

Perl中的$|變數如果被設定為1,那麼之後所有的輸出操作將無視緩衝區的存在而“立即重新整理緩衝區”,那麼輸出的內容會被立即傳送(如果傳送到顯示屏就是立即顯示)

select LOG;
#|=1;   #不要將LOG的內容儲存在緩衝區
select STDOUT;
#...
print LOG "This is something...\n";

以上,比如讀取監視某個耗時程式的實時日誌

Perl5的新增語法特性

1. open函式有三個引數,實現了引數分離,更安全

open CONFIG, '<', 'dino';
open BEDROCK, '>', '$filename';

2. 三個引數允許指定資料的編碼方式

open CONFIG, '<:encoding(UTF-8)', 'dino';
#:encoding(utf-8)簡寫:utf-8但不推薦簡寫

3. 自動檢測致命錯誤

use autodie;

4. 使用say來輸出

use 5.010;
say "Hello!";   #列印變數並自動在末尾新增一個換行符

5. 標量中的檔案控制代碼

從Perl5.6開始,我們可以把檔案控制代碼存放在標量中,而不必非得使用“裸字(字面量)”
在稱為標量之後,檔案控制代碼就可以作為函式引數傳遞給子程式

my $rocks_fh   #使用詞法變數my確保該變數之前是空的
open $rocks_fh, '<', 'rocks.txt'
    or die "Could not open rocks.txt: $!";

或者

open my $rocks_fh, '<', 'rocks.txt'
    or die "Could not open rocks.txt: $!";

在得到儲存檔案控制代碼的變數後,直接把原先使用裸字的地方就可以了

while(<$rocks_fh>){
    chomp;
    ...
}

輸出資訊到檔案控制代碼也可以使用到這個變數,在原先使用裸字的地方使用這個標量便能以適當的方式開啟該檔案

open my $rocks_fh, '>>', 'rocks.txt'
    or die "Could not open rocks.txt: $!";

foreach my $rock(qw(slate lava granite)){
    say $rocks_fh $rock
}
print $rocks_fh "limestone\n";
print $rocks_fh;

注意,Perl能夠自動判斷,如果print語句的第一個引數之後沒有逗號,就說明它是一個檔案控制代碼(不!要!加!逗!號!)

print $rocks_fh, "limestone\n";    #錯誤!!(無法判斷檔案控制代碼)

一般短小的程式,比如系統管理員的工具指令碼,用裸字也沒什麼不好;不過對於大一點的專案或應用程式來說,使用標量可以精確控制檔案控制代碼的作用域,方便除錯和維護