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 denied
、file 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"; #錯誤!!(無法判斷檔案控制代碼)
一般短小的程式,比如系統管理員的工具指令碼,用裸字也沒什麼不好;不過對於大一點的專案或應用程式來說,使用標量可以精確控制檔案控制代碼的作用域,方便除錯和維護