在這個部份 Perl tutorial 我們要來看看 如何在 Perl 中讀取一個檔案.

在這裡我們著重在文字檔的部份。

有兩個常見的方法來打開一個檔案, 選擇哪一種方式跟你要如何處理錯誤訊息有關。

執行

Case 1: 如果不能打開檔案則丟出例外訊息:

use strict;
use warnings;

my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
  or die "Could not open file '$filename' $!";

while (my $row = <$fh>) {
  chomp $row;
  print "$row\n";
}

警示或是不丟出訊息

Case 2: 如果無法打開檔案則丟出警示訊息但還可以繼續執行程式:

use strict;
use warnings;

my $filename = 'data.txt';
if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
  while (my $row = <$fh>) {
    chomp $row;
    print "$row\n";
  }
} else {
  warn "Could not open file '$filename' $!";
}

解說

讓我們來嘗試看看以上所說的:

首先,用文字編輯器來製造一個名叫 'data.txt' 的文字檔,然後在檔案中寫入以下文字:

First row
Second row
Third row

打開並且讀取檔案跟 寫入檔案 很相似, 但是用 "<" 符號來取代 ">" 符號。(<)

這裡我們使用 UTF-8 來當作編碼。在這裡讀取檔案只會出現 "<" 符號。

use strict;
use warnings;

my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
  or die "Could not open file '$filename' $!";

my $row = <$fh>;
print "$row\n";
print "done\n";

一旦我們有檔案符號,我們就能跟使用 從鍵盤讀取(STDIN) 資料一樣的操作方式來從檔案符號讀取資料(readline)。. 上面的程式碼會讀取檔案的第1行資料。然後我們可以印出在 $row 的內容資料,再印出 "done"。

如果你執行上面的程式碼你會看到印出

First row

done

你也許會問為何在 "done" 之前有一行空白列。 這是因為 readline 這個操作子會讀取一整列資料,包括最後的換行。當我們使用 print()來印出資料時,我們又加了第2個換行資料。

如同從 STDIN 讀取資料一樣,我們都常不需要最後的換行資料,所以我們可以使用 chomp() 來移除它。

讀取更多列資料

一旦我們知道如何讀取一行資料,我們可以把 readline 放在 while 迴圈來讀取更多列資料。

use strict;
use warnings;

my $filename = $0;
open(my $fh, '<:encoding(UTF-8)', $filename)
  or die "Could not open file '$filename' $!";

while (my $row = <$fh>) {
  chomp $row;
  print "$row\n";
}
print "done\n";

每一次我們執行到 while 迴圈時,它會先執行 my $row = <$fh>,這樣來取得下一列資料。 如果這一列有任何資料的話,那麼它會為真值。即使是空的一列,在結束的時候也會有換行符號。 這樣我們讀到的時候,$row 這個變數會包含 \n,這樣就會為真值。

在我們讀到最後一列之後,接下來 readline 會讀取到 (<$fh>) 而回傳 undef,這是偽值。所以 while 迴圈就結束了。

邊界案例(edge-case)

有一個邊界案例是:當檔案的最後是 0 而且沒有換行符號。這樣程式可能會判斷成偽值,所以 while 迴圈不會被執行。 不過在這個案例中 Perl 作弊了!如果 Perl 用 while (defined my $row = <$fh>) 來讀取這個邊界案例的話,會執行的很好。and so even such lines will execute properly.

打開檔案而不使用 die

上面這個方法適合使用在你只有打開檔案而不做其他事情。比如說,整個程式就只用來分析這個檔案。

如果這是一個選擇性的配置檔案呢? 如果你能讀取這個配置檔案,那就改變某些設定。如果讀取失敗,那就使用預設值就好。

如果你考慮的是這樣的方式,那麼下面程式也許是一個好的方法。

if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
  while (my $row = <$fh>) {
    chomp $row;
    print "$row\n";
  }
} else {
  warn "Could not open file '$filename' $!";
}

在這個方法中,我們檢驗 open的回傳值。 如果為真,我們就繼續讀取檔案資料。如果為假,我們就使用 warn 這個函數來顯示警示,而不丟出例外。 甚至在這裡我們可以不用 else

if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
  while (my $row = <$fh>) {
    chomp $row;
    print "$row\n";
  }
}