標籤:
第十章 格式化輸出
by flamephoenix
一、定義列印格式
二、顯示列印格式
三、在列印格式中顯示值
1、通用的列印格式
2、格式和局域變數
3、選擇範圍格式
4、輸出範圍字元
四、輸出到其它檔案
五、分頁
六、格式化長字串
七、用printf格式化輸出
我們已經見過用print函數將原始的未格式化的文本輸出到檔案,本章講述如何用函數write和列印格式來產生格式化的輸出。
二、顯示列印格式
列印格式的顯示有兩步:
1、將系統變數$~設成所要使用的格式
2、調用函數write
例如:
1 : #!/usr/local/bin/perl
2 :
3 : $~ = "MYFORMAT";
4 : write;
5 :
6 : format MYFORMAT =
7 : ===================================
8 : Here is the text I want to display.
9 : ===================================
10: .
結果輸出如下:
$ program
===================================
Here is the text I want to display.
===================================
$
如果不用$~指定列印格式,Perl解譯器就假定要使用的格式名與要寫入的檔案變數同名,在本例中,如果不指定使用MYFORMAT,則Perl解譯器試圖使用名為STDOUT的列印格式。
三、在列印格式中顯示值
我們使用列印格式的主要原因當然是格式化存貯在簡單變數或陣列變數中的值從而產生可讀性好的輸出,這一目的用“範圍”來實現。每個範圍指定一個值,如變數或運算式,調用write函數時,該值就以範圍指定的格式顯示。
1、通用的列印格式
列印格式的一個缺點是定義中包含了變數名,例如:
format MYFORMAT =
==========================================================
The winning number is @<<<<<<!
$winnum
==========================================================
.
當調用write輸出此格式時,必須記著它使用了變數$winnum。用子程式和局域變數就可以建立更通用的列印格式。下例從STDIN輸入一個檔案並輸出五個出現頻率最高的字母及出現次數。
1 : #!/usr/local/bin/perl
2 :
3 : while ($line = ) {
4 : $line =~ tr/A-Z/a-z/;
5 : $line =~ s/[^a-z]//g;
6 : @letters = split(//, $line);
7 : foreach $letter (@letters) {
8 : $lettercount{$letter} += 1;
9 : }
10: }
11:
12: $~ = "WRITEHEADER";
13: write;
14: $count = 0;
15: foreach $letter (reverse sort occurrences
16: (keys(%lettercount))) {
17: &write_letter($letter, $lettercount{$letter});
18: last if (++$count == 5);
19: }
20:
21: sub occurrences {
22: $lettercount{$a} <=> $lettercount{$b};
23: }
24: sub write_letter {
25: local($letter, $value) = @_;
26:
27: $~ = "WRITELETTER";
28: write;
29: }
30: format WRITEHEADER =
31: The five most frequently occurring letters are:
32: .
33: format WRITELETTER =
34: @: @<<<<<<
35: $letter, $value
36: .
運行結果如下:
$ programThis is a test file.This test file contains some input.The quick brown fox jumped over the lazy dog.^DThe five most frequently occurring letters are: t: 10 e: 9 i: 8 s: 7 o: 6$
2、格式和局域變數
在上例中,你可能已經注意到子程式write_letter調用write輸出字母及其出現次數,即使格式定義在子程式外部仍能正常工作。在第17行中將字母及其出現次數傳遞給該子程式,在子程式中,列印格式使用局域變數$letter和$value,這樣保證了在foreach迴圈中每次輸出當前的字母和值。
然而要注意的是,使用my定義的局域變數要求格式定義在子程式內部,否則就不會輸出,因此,用write輸出的局域變數一定要用local定義。(local和my詳見《子程式》一章)
註:Perl4中沒有my函數,故不會有此問題。
3、選擇範圍格式
我們已經知道了列印格式和write函數怎麼工作,現在來看看範圍的格式,見下表:
| 格式 |
範圍含義 |
| @<<< |
靠左對齊輸出 |
| @>>> |
靠右對齊輸出 |
| @||| |
中對齊輸出 |
| @##.## |
固定精度數字 |
| @* |
多行文本 |
每個範圍的第一個字元是行填充符,當使用@字元時,不做文字格式設定化。對文本的格式化稍後來講。
在上表中,除了多行範圍@*,域寬都等於其指定的包含字元@在內的字元個數,例如:
@###.##
表示七個字元寬,小數點前四個,小數點後兩個。
4、輸出範圍字元
在列印格式裡,特定字元如@、<和>被看作範圍定義,那麼如何將它們輸出呢?方法如下:
format SPECIAL =
This line contains the special character @.
"@"
.
四、輸出到其它檔案
預設地,函數write將結果輸出到標準輸出檔案STDOUT,我們也可以使它將結果輸出到任意其它的檔案中。最簡單的方法就是把檔案變數作為參數傳遞給write,如:
write (MYFILE);
這樣,write就用預設的名為MYFILE的列印格式輸出到檔案MYFILE中,但是這樣就不能用$~變數來改變所使用的列印格式。系統變數$~只對預設檔案變數起作用,我們可以改變預設檔案變數,改變$~,再調用write,例如:
select (MYFILE);
$~ = "MYFORMAT";
write;
當select改變預設檔案變數時,它返回當前預設檔案變數的內部表示,這樣我們就可以建立子程式,按自己的想法輸出,又不影響程式的其它部分,如下:
sub write_to_stdout {
local ($savefile, $saveformat);
$savefile = select(STDOUT);
$saveformat = $~;
$~ = "MYFORMAT";
write;
$~ = $saveformat;
select($savefile);
}
五、分頁
在輸出到印表機時,可以在每頁頂部輸出相應的資訊,這樣的特殊文本叫頁首。定義頁首實際上就是定義名為filename_TOP的列印格式,例如給標準輸出檔案定義頁首如下:
format STDOUT_TOP =
Consolidated Widgets Inc. 1994 Annual Report
.
在頁首的定義中也可以包含範圍,頁首中經常使用的一個特殊值是當前頁碼,存貯在系統變數$%中,如:
format STDOUT_TOP =
Page @<<.
$%
.
我們也可以通過改變系統變數$^改變定義頁首的列印格式名,與$~一樣,$^只對當前預設檔案起作用,因此可以與select函數結合使用。
預設情況下,每頁長度為60行,可以通過改變$=來改變頁長,如:
$= = 66; #頁長設為66行
此指派陳述式必須出現在第一個write語句前。
註:一般使用分頁機制時不用print函數,因為當用write輸出時,Perl解譯器跟蹤每頁的當前行號。如果必須使用print而又不打亂頁計數,可以調整系統變數$-。$-的含義是當前行到頁末之間的行數,當$-達到零時,就開始新的一頁,調整方法如:
print ("Here is a line of output\n");
$- -= 1;
六、格式化長字串
我們已經學過範圍@*可以輸出多行文本,但它完全將字串原樣輸出,不加以格式化。在Perl中對長字串(包含換行)進行格式化的範圍定義很簡單,只需把打頭的@字元換成^就行了,這種文字格式設定化中,Perl解譯器在一行中放置儘可能多的單詞。每當輸出一行文本,被輸出的子串就從變數中刪除,再次在域值中使用該變數就把剩下的字串繼續按格式輸出。當內容已輸出完畢,該變數就成了空串,再輸出就會輸出空行,為避免輸出空行,可以在範圍格式行首加一個~字元。見下例:
1 : #!/usr/local/bin/perl
2 :
3 : @quotation = <STDIN>;
4 : $quotation = join("", @quotation);
5 : $~ = "QUOTATION";
6 : write;
7 :
8 : format QUOTATION =
9 : Quotation for the day:
10: -----------------------------
11: ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
12: $quotation
13: ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
14: $quotation
15: ~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
16: $quotation
17: -----------------------------
18: .
運行結果如下:
$ program
Any sufficiently advanced programming
language is indistinguishable from magic.
^D
Quotation for the day:
-----------------------------
Any sufficiently advanced programming language is
indistinguishable from magic.
-----------------------------
$
如果把列印格式中行首的~字元去掉,上面的輸出結果中就會多一行空行。很明顯,當字串長度不明確時,這種用法很不方便,原因就在於它指明了輸出的行數上限,超過這一上限的字元就不會被輸出,解決方案很簡單,就是在域值格式行首加兩個~字元,這樣就會持續按格式輸出文本直到輸出完畢,用此方法把上述程式改寫如下:
1 : #!/usr/local/bin/perl
2 :
3 : @quotation = <STDIN>;
4 : $quotation = join("", @quotation);
5 : $~ = "QUOTATION";
6 : write;
7 :
8 : format QUOTATION =
9 : Quotation for the day:
10: -----------------------------
11: ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
12: $quotation
13: -----------------------------
14: .
這樣運行結果相同。
七、用printf格式化輸出
還有一種容易掌握和使用的格式化輸出函數,那就是printf,它與C語言中的printf基本上是相同的。printf有兩個參數,一個是字串,其中含有一個或多個域值形式,另一個是與各域值相對應的變數值按一定格式替換,如:
printf("The number I want to print is %d.\n", $number);
各種域值形式如下表:
| 域值 |
含義 |
| %c |
單個字元 |
| %d |
十進位整數 |
| %e |
科學計數法形式的浮點數 |
| %f |
普通形式(定點)浮點數 |
| %g |
緊縮形式浮點數 |
| %o |
八進位整數 |
| %s |
字串 |
| %u |
不帶正負號的整數 |
| %x |
十六進位整數 |
一些使用細節如下:
1、在格式d、o、u或x中,如果整數值較大或可能較大,可加個l字元,意為長整型,如%ld。
2、%字元後加正整數表示該域的最小寬度,如果輸出結果寬度不足,則向靠右對齊,前面用空格補足,如果該正整數以數字0打頭,則補足字元為0。若%字元後為負整數,則結果向靠右對齊。
3、浮點數域值(%c、%f和%g)中可以指定小數點前後的寬度,如%8.3f意為總寬度為8個字元,小數點後(即小數部分)為3個字元,多出的小數部分四捨五入。
4、在整數、字元或字串的範圍中使用如上的小數形式n.m,整數部分n為總寬度,小數部分m為輸出結果的最大寬度,這樣就保證了輸出結果前至少有n-m個空格。
上一章 下一章 目錄
perl5 第十章 格式化輸出