perl输入输出

1.从标准输入设备输入

<STDIN>为行输入操作,但其实际上是对一个文件句柄(filehandle)的行输入操作(有<>表示)。

$line = <STDIN>; #读入下一行;
chomp($line); #去掉结尾的换行符
chomp($line=<STDIN>) #同上,更常用的方法

由于,行输入操作在到达文件的结尾时将返回undef,这对于从循环退出时非常方便的:
while (defined($line = <STDIN>)) {
print “I saw $line”;
}
第一行代码值得仔细说明:我们将输入的字符串读入一个变量,检查其是否defined,如果是(意味着我们没有到达输入的
结尾),则执行while的循环体。因此,在循环的内部,我们将看到每一行,一行接着一行◆。你可能经常进行此类操作,
因此Perl提供了一种简写方式。如下:

while(<STDIN>){
print “I saw $_”;
}

你也可以这样写:

while (defined($_ = <STDIN>)){
print“I saw $_”;
}

在进行深入讨论前,我们要澄清一些事:这种简写只在特定的情况下有效,默认的情况下不会将一行读入变量$_。仅当while
循环的条件判断部分只包含行输入操作才有效◆。如果在条件判断部分还有别的内容,则上述简写无效。

除此之外,行输入操作(<STDIN>)和Perl的默认变量($_)没有别的关系。在本例中,输入的值被保存在变量$_中。

另一方面,在列表context中使用行输入操作时,则会将所有的行(剩下的)当作一个列表,而每一行作为列表的一个元素:
foreach(<STDIN>){
print“I saw $_”;
}

2.从<>输入

另一种方法是使用尖括号◆输入(diamondoperator):<>。这种方法对于书写类似于标准Unix◆工具的程序非常有用。如果
想写一个Perl程序,使它具有像cat, sed, awk, sort, grep, lpr, 以及许多别的应用程序类似的功能,则<>将帮上你的大忙。对
于其它方面,<>可能帮不上你什么。

一个程序的调用参数(invocation arguments),通常是命令行中程序名字后面的一些“字符串”◆。下例中,它们是要被顺

序处理的文件的名字:

◆当程序开始运行运行时,它有0个或多个调用参数,这由此程序决定。这通常出现在shell中,此列表由你命令行中输入的内容决定。在
后面将看到,调用程序可以使用许多字符串作为调用参数(invocation arguments)。由于它们经常出现在命令行中,因此有时亦被称作命
令行参数(command-line arguments)。

$ ./my_program fred barney betty

上述命令的含义是,运行my_program(在当前目录下),它将处理文件fred,再处理文件barney,最后是文件betty。

如果没有命令行参数,程序将处理标准输入流(standard input stream)。作为一个特例,如果将连接号(-)作为一个参数,其
含义也是标准输入◆。如果调用参数为fred –betty,其含义是程序将首先处理文件fred,其次是标准输入流,最后是文件betty。

◆这是Unix中很少人知道的一个事实:如许多标准的工具,如cat, sed, 也使用这种约定,连接号(-)代表标准输入流。

使用这种方法来书写程序的一个好处是,可以在程序运行时决定其输入。例如,你不需要重写它就可以在管道(pipeline)
中使用(将在后面讨论)。Larry将它引入Perl,是因为他希望可以容易的写你自己的程序,使它们具有标准Unix工具的特
点,甚至是在Non-Unix系统中。事实上,Larry本人就做了大量的此类程序。由于不同工具提供商提供的工具不同,Larry
可以创建他自己的工具,这些工具可以在不同的机器上使用,并且其行为相同的。当然,这意味着要首先将Perl移植到这
些机器上去。

尖括号操作(<>)是一种特殊的行输入操作。其输入可由用户选择◆:

while (defined($line = <>)){
chomp($line);
print “It was &line that I saw!\n”;
}

运行此程序,调用参数为fred,barney,betty,则结果大概如下:“It was [a line from file fred] (文件中fred的一行)that I saw!”,
“It was [another line from file fred](文件fred 中的另一行)that I saw!”,直到文件fred的结尾。然后,将自动转到文件
barney,一行一行的输出,最后到文件betty。从一个文件到另一个文件之间没有空行,当使用<>时,就像输入的是一个大
文件一样◆。如果输入结束时,<>将返回undef(同时退出while循环)。

◆当前的文件名字被保存在Perl的特殊变量$ARGV中。名字“-”代表某个文件,如果其为标准输入流输入。

由于这是一种特殊的行输入操作,我们也可以使用前面的相似的简写方法:
while(<>){
chomp;
print “It was $_ that I saw!\n”;
}

由于<>通常被用来处理所有的输入,因此在同一个序中重复使用是不正确的。如果在同一个程序中使用了2次<>,特别是
在while循环内第二次使用<>读入第一次<>的值,其结果通常不是你所希望的◆。根据我们的经验,当初学者在程序中使
用第二个<>,通常是想使用$_。记住,<>读入输入,但输入内容本身被存储在$_(默认的情形)。

如果<>不能打开文件从中读入,它将打印出一些有用的诊断信息,如:
Can’t openwilma: no such file or directory
<>将自动转到下一个文件,这和cat这个标准工具是一致的。

3.调用参数

技术上讲,<>从数组@ARGV中得到调用参数。这个数组是Perl中的一个特殊数组,其包含调用参数的列表。换句话说,
这和一般数组没什么两样(除了其名字有些特别:全为大写字母),程序开始运行时,调用参数已被存在@ARGV之中了◆。

可以像数组那样使用@ARGV,如使用shift将元素移出,或者使用foreach进行迭代等。甚者可以检查是否某个参数由-开
头,进而可以做为参数选项(invocation options)处理它们(如Perl处理其-w选项)◆。

<>操作查看@argv来决定使用哪些文件。如果表为空,则使用标准输入流;否则,使用其找到的相应文件。也就是说,在
启动程序后,使用<>之前,你还有机会修改@argv的值。例如,下面程序可以处理3个指定的文件,无论用户在命令行中
输入了什么其它的文件:

@argv = qw# larry mor curly #; #强制使用这三个文件
while(<>){
chomp;
print “It was $_ that I saw in some stooge-like file!\n”;
}

4.输出到标准输出设备

当然,打印数组和内插一个数组是不同的:

print @array; #打印出元素的列表
print“@array”; #打印一个字符串(包含一个内插的数组)

第一个语句打印出所有的元素,一个接着一个,其中没有空格。第二个打印出一个元素,它为@array的所有元素,其被存
在一个字符串中。也就是说,打印出@array的所有元素,并由空格分开◆。如果@array包含qw /fred barney betty/◆,则
第一个例子输出为:fredbarneybetty,而第二个例子输出为fred barney betty(由空格分开)。在决定使用第二种方法前,

想象@array为一个unchomped的输入列。也就是说,每一个字符串都有一个换行符作为后缀。
现在第一个print语句输出为fred, barney, betty,分别在三行中。第二个print语句输出为:
fred
barney
betty

你知道空格是哪里来的吗?Perl在内插数组时,它会在元素之间加入空格。我们得到数组的第一个元素(fred和换行符),空
格,数组的第二个元素(barney和换行符),空格,数组的最后一个元素(betty 和换行符)。结果是数组除了第一个元素之外
元素都被缩进了。

print (2+3);

上述代码看起来像函数调用,它实际上就是函数调用。其输出为5,然后返回一个值,这和其它函数调用一样。print返回
值为true 或者false,表明print成功或者失败。通常是成功的,除非遇到I/O错误,因此,下面语句中$result的值通常为1:

$result = print(“hello world\n”);

如果以其它方式来使用返回值呢?假如想把返回值乘以4:
print (2+3)*4; #oops!

当Perl遇到上述代码时,将输出5。然后将print的返回值1,乘以4。其结果被丢弃了,因为没有将其值赋给任何变量。
现在,有些人可能会说:“嘿,Perl不能进行数学运算!应该输出20,而非5!”
这是由括号是可选的引起的;有时,人会忽略括号属于哪一部分。当没有括号时,print是一个列表操作,将后面的所有元
素输出来,这通常是你所希望的。当print后面是一个开括号时,print为函数调用,它将输出括号中的内容。由于上述代码
中有括号,则它等同于下面的代码:
(print (2+3)) * 4; #oops!
幸运的是,如果打开了警告时(warnings),Perl会提示你。因此至少在程序的开发和调试阶段,使用–w或者use warnings,

5.使用printf格式化输出

my @items = qw( wilma dino pebbles );
my $format =“The items are:\n”. (“%10s\n”x @items);
## print“the format is >>$format<<\n”; #用于调试
printf $format, @items;

上述代码中使用了x操作(在第二章中学习过),来确定字符串要重复的次数,此次数有@items确定(此时是在标量context
中使用的)。本例中为3,因为有3个元素,因此上述的格式字符串等同于“The items are:\n10s\n10s\n10s\n”。输出的每一个
元素占一行,右对齐,长度为10。相当酷,对吧?还有更好的方法,可以把它们结合起来使用:
printf“The items are:\n”. (“%10s\n”x @items), @items;
本例中使用了@items两次,一次在标量context中,取其元素的个数,一次在列表context中取其元素。context是相当重要
的。

6.句柄

文件句柄(filehandle)是Perl程序I/O 连接的名字,是Perl和外界的纽带。也就是说,它是连接的名字,而非文件的名字。
文件句柄的命名规则和Perl中其它标识符一样(由字母,数字,下划线组成,但不能由数字开头);由于没有任何的前缀符,
这可能和现在或者将来的保留字,标签(在第十章中介绍)混淆。因此,和标签一样,Larry推荐文件句柄的所有字母均大
写。这有利于阅读,并且能保证程序在引入新的保留字(小写)后仍能正确执行。

Perl自身有六个文件句柄:STDIN,STDOUT,STDERR,DATA,ARGV,ARGVOUT◆。虽然可以任意给文件句柄命名,
但不能选择上面六个,除非你想利用它们的某些特殊性质◆。

你可能还记得其中一些句柄的名字。当程序运行时,STDIN连接Perl当前处理的部分和其他输入来源,其一般被称作标准
输入流。通常,是指键盘,除非指定了别的输入源,如文件或者另一个程序的输出,通过管道(pipe)◆。STDOUT是标准
输出流。默认情况,这是指用户的显示屏,但可以将其输出到文件,或者另一个程序中,我们将很快看到。这些标准流来
源于Unix的标准I/O 库,但它们在大多数的当代操作系统◆中都能正确工作。一般的思想是,程序只是从STDIN读入,
写出到STDOUT,相信用户(或者启动此程序的程序)能正确地设定。由于上述原因,用户就可以在shell提示符中输入如
下的命令:

$ ./your_program <dino >Wilma

上述命令告诉shell,从一个名叫dino读入,将结果输出到叫做wilma的文件之中。由于程序从STDIN读入,处理它(按
照我们的要求),再输出到STDOUT,上述代码将能很好工作。

不需要额外的代价,上述程序能很好的在管道中使用。这是Unix的另一个概念,可以让我们如下的写命令:
$ cat fred barney | sort | ./your_program | grep something | lpr

如果你不熟悉Unix命令,也不要紧。上面一行的含义是:cat命令将输出文件fred 的所有行,紧接着是文件barney的所有
行。这个输出作为sort的输入,它将所有的输入的行进行排序,再将结果传递给your_program。经过your_program处理后,
将结果传给grep,它会将某些行去除掉,然后送给lpr,它会将传给它的数据打印出来。

7.文件句柄的打开

你已经见过了Perl提供的3种文件句柄:STDIN, STDOUT, STDERR。当需要其它的文件句柄时,使用open 操作通知Perl,
Perl再请求操作系统来建立同外部的连接。下面是一些例子:

openCONFIG,“dino”;
open CONFIG,“<dino”;
open BEDROCK,“>fred”;
open LOG,“>>logfile”

第一例中打开了一个名为CONFIG的文件句柄,它指向dino文件。也就是说,文件dino将被打开,其所包含的数据通过
CONFIG传给程序。这和从文件中取数据,也可以通过STDIN得到是类似的,如果在命令行中使用了shell 重定位操作如
<dino。第二例和第一例类似;它和第一例是一样的,只是<明确的指明了“使用这个文件进行输入操作”,虽然默认的
情况就是输入(没有<)◆。

打开文件进行输入,一般不需要使用<, 我们这里介绍的原因是,在第三例中,使用了大于号(>)来表明是文件的输出。此
例中打开文件句柄BEDROCK,输出到新文件fred 中。和shell重定位中使用的大于号一样,我们将输出送到文件fred 中。
如果存在这样的文件,则清空它,并将新的数据写入。

第四个例子中使用了两个大于号(>>)(和shell 一样),它打开一个文件,数据追加到文件后面。也就是说,如果文件存在,
将把新数据添在后面。如果文件不存在,则和大于号(>)一样,创建文件,并把数据写入。这对于日志(log)文件是非常方
便的;程序可以每次添加写的日志到log 文件中。这也是把第四例中的文件句柄叫做LOG,文件叫做logfile 的原因。

open 也能通过返回的是真是假来告诉我们(成功打开为真,打开失败返回假)。你可以如下的
写代码:
my $success = open LOG, “>>logifle”; #将返回值保存在$success中
if(!$success){
#打开失败时
...
}

8.严重错误和die

if(!open LOG, “>>logfile”){
die “Cannot create logfile:$!”;
}
如果open失败,则die将结束程序,并告诉你不能创建logfile。但,消息中的$!是指什么呢?它是系统产生的一些可读的
信息。通常,当系统拒绝了我们的请求(如打开文件),$!将告诉你原因(可能是“权限不够(permission denied)”或者“(文
件不存在)file not found”,针对本例)。

9.使用文件句柄

当某个文件句柄被打开进行输入时,可以像从STDIN中输入一样。例如,读入Unix中的密码文件:
if(! open PASSWD,“/etc/passwd”){
die“How did you get logged in?($!)”;
}
while(<PASSWD>){
chomp;
...
}
在本例中,die后面的消息使用了$!。它被括号括起来了。如你所知的,“行输入操作(line-input operator)”由两部分组成;
尖括号(<>,真正的行输入操作(line-input operator))和尖括号中的文件句柄。

写出(>)或追加的(>>)的文件句柄,可以和print或printf结合使用,如:

print LOG“Captain’s log, stardate 3.14159\n”; #输出到LOG中
printf STDERR“%D percent complete.\n”, $done/$total * 100;

9.1改变默认的输出句柄

默认情况下,如果不指定文件句柄给print(或者printf,这里的内容对两者均适用),则默认会使用STDOUT。但这个默认属
性,可以通过select操作进行更改。如下:
select BEDROCK;
print“I hope Mr. Slate doesn’t find out about this.\n”;
print“Wilma!\n”

一旦选择了(select)了某个文件句柄,则它将变成默认值。但这通常是一个坏主意,因为会扰乱程序的剩余部分,因此在完
成时应当恢复以前的设置◆。默认情况,输出到文件句柄的内容会被缓存起来。将变量$|设置为1,将会在输出操作结束
时会立刻清空文件句柄。如果想确保logfile 能立刻得到消息,以便能观察程序的运行情况,可以使用下面的程序:

select LOG;
$| = 1; #don’t keep LOG entries sitting in the buffer
select STDOUT;
#...time passes, babies learn to work, tectonic plates shift, and then … .
print LOG “This gets written to the LOG at once!\n”;

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值