Perl 格式
Perl 有一个机制帮助你产生简单的报告和图表.为了实现这个机制,Perl 帮助你格式化你的输出,使它打印出来的时候看起来比较接近于你想要的结果.它能保持跟踪象一页里面有多少行,当前的页码,以及什么时候打印页头等等的东西.使用的关键字是从 FORTRAN 里面借来的:format 用来声明而 write 用来执行;参看第二十九章,函数,获取相关内容.所幸,布局时非常易读的,很象 BASIC 中的 PRINT USING 语句.也可以将它想象成 nroff(如果你知道 nroff,这也许不象是一个比较).
格式输出,和包和子过程一样,是声明而不是执行,因此它们可以在你的程序中任何地方出现.(通常最好将所有的格式输出放在一起).它们有他们自己的名字空间,与 Perl 中其它类型的名字空间是区分开来的.这就是说如果你有一个函数 "Foo",但它不同于一个名字为 "Foo" 的格式输出.然而和一个文件句柄相关联的格式输出的缺省名字和该文件句柄的名字相同.因而,STDOUT 的缺省格式输出的名字是 "STDOUT",文件句柄 TEM P的缺省格式输出名字为 "TEMP",它们看起来是一样的,实际上是不一样的.
输出纪录格式输出象下边一样定义:
format NAME =
FORMLIST
.
如果省略 NAME,将定义格式输出 STDOUT.FORMLIST 由一些有序的行组成,每一行都是下面三种类型中的一种:
注释,以第一列为 # 来表示.
一个格式行,用来定义一个输出行的格式
参数行,用来向前面的格式行中插入值
格式行除了那些需要被替换的部分外,严格按照它们的声明被输出.(注:而且,甚至那些你放进去维护列完整性的域也如此.在一个格式行中没有任何东西可以导致域的伸缩或者移位.你看到的列是按照 WYSIWYG 的概念分布的---假设你用的是定宽字体.就连控制字符都假设是宽度为一的字符.)格式行中每个被替换的部分分别以 @ 或者 ^ 开头.这些行不作任何形式的变量代换.@ 域(不要同数组符号 @ 相混淆)是普通的域.另一种域,^ 域用来进行多行文本块填充.域的长度通过在格式符号 @,^ 后跟随特定长度的 <, >,| 来定义,同时,<,>,| 还分别表示,左对齐,右对齐,居中对齐.如果变量超出定义的长度,那么它将被截断.
作为右对齐的另外一种方式,你可以使用 #(在 @ 或 ^ 后边)来指定一个数字域.你可以在这种区域中插入一个 . 来制定小数点的位置.如果这些区域的值包含一个换行符,那么只输出换行符前面的文本.最后,特殊的域 @* 可以被用来打印多行不截断的值;这种区域通常在一个格式行中出现.
参数行指定参数的顺序必须跟相应的格式行的域顺序一致.不同参数的表达式需要使用逗号分隔.参数行被处理之前所有的参数表达式都在列表环境中求值,因此单个列表表达式会产生多个列表元素.通过使用圆括弧将表达式括起来,可以使表达式扩展到多行 (因此,圆括弧必须是第一行的第一个标志).这样就可以将值同相应的格式域对应起来方便阅读.
如果一个表达式求出的值是一个有小数部分的数字,并且如果对应的格式域指定了输出的小数部分的格式(除了没有内嵌 . 的多个 # 字符的格式),用来表示小数点的字符总是由 LC_NUMERIC 区域参数确定.这就是,如果运行时环境恰好是德国本地化参数,一个逗号总是用来替代句点.参看 perllocale 手册页获取更多的内容.
在一个表达式中,空白字符 \n,\t,和 \f 总是被解释成单个空格.因而,你可以认为这样的过滤表达式作用于每个格式中的表达式:
$value =~ tr/\n\t\f/ /;
余下的空白字符,\r, 如果格式行允许的话,将强制输出一个换行符.
以 ^ 开头的格式域不同于 @ 格式域,它会被特殊对待.例如一个 # 区域,如果值没有定义,那么这个区域将变为空白.对于其他的区域类型,^ 会使用一种特殊的填充模式.提供的值必须是一个包含字符串的标量变量名,而不是一个强制表达式.Perl 在这个区域中放入尽可能多的文本,并且将已经打印过的字符截去,这样当下次引用该变量的时候,就可以打印更多的文本.(这就是说在 write 调用过程中,变量本身将发生变化,原来的值将不被保留.因此如果你想保持最初的值,你需要使用一个临时变量来代替原来的变量).通常你应该使用一组垂直对齐的格式区域打印一块文本.你也许会想用文本 "..." 来结束最后的区域,这样当文本太长不能完整地打印的时候,指定的文本将会被打印.你也可以改变变量 $:(当你使用 English 模块,那么就是 $FORMAT_LINE_BREAK_CHARACTERS)来改变用来表示中断的合法字符.
使用 ^ 区域能够产生变长的纪录.如果格式区域太短,你可以重复几次带有 ^ 区域的格式行.如果你用这个方法处理一块较短的数据,那么你将会得到几个空白输出行.为了避免空白行,你可以在格式行中的任意地方放置一个 ~ (波浪号).(在输出中波浪号本身会被转换成一个空格).如果你使用第二个 ~ 波浪号,该格式行会被重复直到在该格式行中所有域中的文本被用尽为止.(因为 ^ 区域会吃掉所要打印的字符串,因此前面的格式行能够运行,但是如果你使用和两个波浪号结合的一个 @ 域,你最好每次给这表达式不同的值!使用 shift 或者其他带有副作用的操作符,来用尽所有的值.)
标头的处理缺省使用当前文件句柄名加上 _TOP 后缀的格式来处理.它在每一页的开头被触发.参看二十九章的 write.
# a report on the /etc/passwd file
format STDOUT_TOP =
Passwd File
Name Login Office Uid Gid Home
------------------------------------------------------------------
.
format STDOUT =
@<<<<<<<<<<<<<<<<<< @||||||| @<<<<<<@>>>> @>>>> @<<<<<<<<<<<<<<<<<
$name, $login, $office,$uid,$gid, $home
.
# a report from a bug report form
format STDOUT_TOP =
Bug Reports
@<<<<<<<<<<<<<<<<<<<<<<< @||| @>>>>>>>>>>>>>>>>>>>>>>>
$system, $%, $date
------------------------------------------------------------------
.
format STDOUT =
Subject: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$subject
Index: @<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$index, $description
Priority: @<<<<<<<<<< Date: @<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$priority, $date, $description
From: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$from, $description
Assigned to: @<<<<<<<<<<<<<<<<<<<<<< ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$programmer, $description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$description
~ ^<<<<<<<<<<<<<<<<<<<<<<<...
$description
.
除非一个格式是在词法变量的作用范围内定义,否则该词法变量在格式中是不可见的.
在同一个输出通道中将 print 和 write 混合起来是可以的,但是你必须自己操作特殊变量 $- (在 English 模块中是 $FORMAT_LINES_LEFT).