第十章 格式化輸出
|
| 我們已經見過用print函數將原始的未格式化的文本輸出到文件,本章講述如何用函數write和打印格式來生成格式化的輸出。 |
■【 定義打印格式 】 |
定義打印格式的關鍵字為format,語法為︰ format formatname = lines_of_output . 打印格式名formatname的命名規則與一般變量相同。結束行為一個句號,此行不能有任何其它字符,包括空格,句號必須是該行第一個字符。 打印格式的定義象子程序一樣可以放在任何地方,甚至可以放在條件語句中,但是最好把它們集中放在程序的開頭或結尾。 |
■【 顯示打印格式 】 |
打印格式的顯示有兩步︰ 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: . 運行結果如下︰ $ program
This is a test file.
This test file contains some input.
The quick brown fox jumped over the lazy dog.
^D
The 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個空格。 |
 CopyRight © 2001蜀 漢 皇 朝All Rights Reserved |