輸入與輸出
讀取標準輸入
▲
之所以稱<STDIN>為行輸入操作符,是因為它真的是每次讀取一行,用一對尖括號表示。被它包圍的檔案控制代碼表示資料來源。
▲
由於行輸入操作符在讀到檔案結尾,沒有可讀內容的時候會返回undef,所以藉此可以跳出迴圈。
while(defined($line = <STDIN>)){ print "I saw $line"; }
由於這樣的額操作很常見,Perl提供了簡寫形式:
while(<STDIN>){ print "I saw $_"; }
完整展開的等效寫法:
while (defined($_ = <STDIN>)){ print "I saw $_"; }
注意:
1.這個簡寫行為只有在上面這種寫法中才適用。如果把行輸入操作符放在其他任何地方,特別是單獨成行時,它不會讀取一行輸入並存到$_。
唯獨while迴圈的條件表示式中只有行輸入操作符時,才會按照這個方式執行。換句話說,若條件表示式中寫了其他程式碼,此行為將失效。
2.行輸入操作符(<STDIN>)和Perl的預設變數$_之間並無直接關聯,只是在這個簡寫裡,輸入的內容恰好存到預設變數中而已。
▲
比較這兩個迴圈的差別:
while(<STDIN>){ print"I saw $_"; }
和
foreach(<STDIN>){ print "I saw $_"; }
區別:
在while迴圈裡,Perl每次讀取一行輸入,把它存到某個變數並執行迴圈體,而後再嘗試獲取後續輸入。
但在foreach迴圈裡,行輸入操作符會按列表上下文處理,所以要做的第一件事就是把所有的輸入都讀進來,按列表返回。資料量小的話沒關係,如果輸入來自一個400M的檔案,就要準備相應大小的記憶體來安頓!所以沒啥特殊情況,儘量還是使用while迴圈,一次處理一行。
來自鑽石操作符的輸入
另一種讀取輸入的方法:鑽石操作符<>。
一般來說,“鍵盤”就是標準輸入,“螢幕”就是標準輸出。
▲
鑽石操作符<>,能讓程式在處理呼叫引數時,提供類似於標準Unix工具程式的功能。
如果想用Perl編寫類似cat、sed、awk、sort、grep、lpr之類的工具程式,一邊從標準輸入拿資料,一邊輸出結果到標準輸出,鑽石操作符將會是你的好幫手。
但如果要支援複雜的引數,單單靠鑽石操作符也是不行的。
▲
$ ./my_program fred barney betty
這條命令的含義:
執行當前目錄的my_program程式,讓它依次處理檔案fred、barney和betty(當然,這樣寫的話,檔案也要在程式執行的目錄下)
▲
若不提供任何引數,程式會從標準輸入流採集資料(也就是“鍵盤”)。
▲
如果引數是連字元(-),則表示從標準輸入(也就是“鍵盤”)讀取資料。
所以,假如呼叫引數是fred - betty,那麼程式應該先處理檔案fred,然後處理標準輸入流中提供的資料,最後才是檔案betty。
▲
鑽石操作符是行輸入操作符的特例,不過它並不是從鍵盤取得輸入,而是從使用者指定的位置讀取。
while (defined($line = <>)){ chomp($line); print "It was $line that I saw!\n"; }
鑽石操作符只有在碰到所有輸入的結尾時才會返回undef(然後程式就跳出while迴圈了)。
▲
當前正在處理的檔名會被儲存在特殊變數$ARGV中。如果當前是從標準輸入獲得資料,那麼當前檔名就會是連字元“-”。
▲
由於鑽石操作符是行輸入操作符的一種特例,因此可以只用之前的簡寫,將輸入讀取到預設變數$_裡:
while (<>){ chomp; #不加引數時,chomp會直接作用在$_上 print "It was $_ that I saw!\n"; }
雙鑽石操作符
▲
在5.22版本的時候,Perl修復了鑽石操作符的一個問題:如果命令列上傳入的檔名帶有特殊字元,比如|,就會引發管道操作。Perl會開啟管道,從外部程式的輸出結果讀取輸入內容。所以這個過程啟動另外一個外部程式,而非開啟某個名字的檔案。
use v5.22; while (<<>>){ chomp; print "It was $_ that I saw!\n"; }
如果是Perl 5.22及以後的版本,最好預設使用雙鑽石操作符。
呼叫引數
▲
從技術上講,鑽石操作符並不會檢查命令列引數,它看到的不過是放在@ARGV數組裡面的資料。
@ARGV是由Perl直譯器事先建立的特殊陣列,並在程式執行前根據命令列引數初始化。所以本質上它的使用方式和其它陣列沒有什麼不同(除了用了奇怪的全大寫名稱之外),只不過在程式開始執行時@ARGV裡就已經塞滿了呼叫引數。
▲既然@ARGV和其他陣列一樣,就可以用shift移出@ARGV中的元素,或者用foreach逐個處理,也可以檢查是否有引數是以連字元(-)開頭的,然後將它們當成呼叫選項處理(就像Perl對待自己的-w選項一樣)。
▲
鑽石操作符會檢視陣列@ARGV,然後決定該用哪些檔名,如果它找到的是空列表(也就是執行時沒有給引數),就會改用標準輸入流,否則,就會使用@ARGV裡的檔案列表。
NOTE:
程式執行之後,只要尚未使用鑽石操作符,就可以對@ARGV動點手腳,可以這樣處理三個特定的檔案,不管使用者在命令列引數中制定了什麼:
@ARGV = qw# larry moe curly #; #強制讓鑽石操作符只讀取這三個檔案 while(<>){ chomp; print "It was $_ that I saw.\n"; }
輸出到標準輸出
▲直接使用陣列和使用陣列內插在列印效果上是不同的:
print @array; #元素間沒有空格分隔 print "@array"; #元素間有空格分隔
注意:
在使用陣列內插的寫法時,如果每個元素都是以換行符結尾,會輸入如下結果:
fred
barney
betty
因為Perl把陣列內插到字串中時,會在每個元素之間加上空格(實際上是特殊變數$”定義的內容,預設是空格字元)。
▲
print處理的是待列印的字串列表,因此它的引數會在列表上下文中被執行。
而鑽石操作符(行輸入操作符的特殊形式)在列表上下文中會返回由許多輸入行組成的列表,所以她們可以配合工作。
print <>; #相當於Unix下的/bin/cat命令 print sort <>; #相當於Unix下的/bin/sort命令
▲
print後面的括號其實可有可無。原則:除非會改變表示式意義,否則括號可以省略。
print("Hello,world!\n"); print "Hello,world!\n";
▲
假如print呼叫看起來像函式呼叫,那它就是一個函式呼叫。
呼叫函式時,函式名後面必須緊接著一對括號,包含呼叫函式的引數。
print (2+3);
它會輸出5,然後和其他函式一樣返回某個值。print的返回值不是真就是假,代表print是否執行成功,通常就是返回1。
$result = print("hello, world!\n"); #返回1
注意:
print (2+3)*4 #糟糕!會列印5
它會列印5,然後Perl會取得print的返回值1,再將這個值乘以4,然後又將這項乘積丟失。
沒有括號的時候,print是列表操作符,會把其後的列表裡的所有東西全都輸出來。
假如print後面緊跟著左括號,它就是一個函式呼叫,只會將括號內的東西輸出來。
因為上面的程式碼有括號,所以就相當於這樣寫:
(print(2+3))*4; #糟糕!
如果使用了-w 或者加上use warnings,Perl就會給出提醒。
修復的辦法:
print ((2+3)*4);
用printf格式化輸出
printf是控制能力比print更強的操作符。
▲
printf操作符的引數包括“格式字串”及“要輸出的資料列表”。格式字串就是用來填空的模板,代表你想要的輸出格式。
printf "Hello, %s; your password expires in %d days!\n",$usr, $days_to_die;
格式字串裡可以有多個轉換,每種轉換都會以百分符號(%)開頭,然後以某個字母結尾(在這兩個符號之間可以存在另外的格式定義)。
後面的列表裡元素的個數應該和轉換的數目一樣多,如果數目不一樣,就無法正確執行。
▲
printf常用轉換格式:(可以檢視perlfunc文件的<sprintf>部分)
(1)%g
要以恰當的形式輸出某個數字,可以使用%g,它會按需要自動輸出浮點數、整數甚至是指數形式。
printf "%g %g %g\n", 5/2,51/17,51**17; #2.5 3 1.0683e+029
(2)%d、%x、%o
%d表示十進位制整數,它會捨去小數點後的數字。注意,它會無條件截斷,而非四捨五入。
printf "in %d days!\n", 17.85; #輸出:in 17 days!
%x表示十六進位制數,%o表示八進位制數。
printf "in %x days!\n",17; #輸出:in 11 days! printf "in %o days!\n",17; #輸出:in 21 days!
(3)指定寬度,如果資料太長,欄位會按需要自動擴充套件。
printf "%6d\n", 42; #輸出:````42(`代表一個空格) printf "%3d\n", 2001.95; #輸出:2001
(4)%s
%s代表字串格式,它的功能其實就是字串內插,但同時還支援設定字元寬度。
printf "%10s\n", "wilma"; #輸出:`````wilma
如果寬度欄位是負數,則表示左對齊(適用於上述各種轉換格式):
printf "%-15s\n", 'flintstone'; #輸出:flintstone`````
(5)%f
%f表示浮點數轉換格式,它可以按需四捨五入,甚至可以指定小數點後的位數:
printf "%12f\n",6*7+2/3; #輸出:```42.666667 printf "%12.3f\n",6*7+2/3; #輸出:``````42.667 printf "%12.0f\n",6*7+2/3; #輸出:``````````43