1. 输入与输出
1.1 从标准输入设备输入
由于,行输入操作在到达文件的结尾时将返回undef,这对于从循环退出时非常方便的:
while (defined($line = <STDIN>)) {
print “I saw $line”;
}
可以这样简写:
while(<STDIN>){
print “I saw $_”;
}
下面这个东西和上面表面上没有区别,实际上它是先得到列表的所有元素再打印的,这样如果是文件很大,就很浪费资源,而上面的就很好,动态利用内存,一个元素一个元素的处理。
foreach(<STDIN>){
print “I saw $_”;
}
1.2 从<>输入
$ ./my_program fred barney betty
上述命令的含义是,运行my_program(在当前目录下),它将处理文件fred,再处理文件barney,最后是文件betty。
如果没有命令行参数,程序将处理标准输入流(standard input stream)。作为一个特例,如果将连接号(-)作为一个参数,其
含义也是标准输入◆。如果调用参数为fred –betty,其含义是程序将首先处理文件fred,其次是标准输入流,最后是文件betty。
while (defined($line = <>)){
chomp($line);
print “It was &line that I saw!/n”;
}
应该想得到,<STDIN>代表标准输入,那么<>就应该代表所有有可能的输入,可以是文件,可以是标准输入。
可以简化的很牛逼:
while(<>){
chomp;
print “It was $_ that I saw!/n”;
}
1.3 调用参数
实际上,<>从数组@ARGV 中得到调用参数。这个数组是Perl 中的一个特殊数组,其包含调用参数的列表。那程序名存在哪里了呢?和shell差不多,在$0里面。
@argv = qw# larry mor curly #; #强制使用这三个文件
while(<>){
chomp;
print “It was $_ that I saw in some stooge-like file!/n”;
}
1.4 Print输出到标准输出
print @array; #打印出元素的列表
print “@array”; #打印一个字符串(包含一个内插的数组)
第一个语句打印出所有的元素,一个接着一个,其中没有空格。第二个打印出一个元素,它为@array 的所有元素,其被存在一个字符串中。也就是说,打印出@array 的所有元素,并由空格分开◆。如果@array 包含qw /fred barney betty /◆,则第一个例子输出为:fredbarneybetty,而第二个例子输出为fred barney betty(由空格分开)。
例子:
#!/usr/bin/perl
@array = <STDIN>;
print @array;
print "@array";
输出:
legend@ubuntu:~/data/study/perl$ perl 5.4
legend1
legend2
legend3
legend1
legend2
legend3
legend1
legend2
legend3
其中前三项是我自己的输入,之后是ctrl+d结束输入。后面六个是程序输出。由于他们是unchomp的,所以每个元素后面都有换行。注意最后两个空格,是数组作为字符串时系统自动添加的。
所以如果,字符串包含换行符,如果想输出它们,通常:
print @array;
如果不包含换行符,通常想加上一个:
print “@array/n”;#注意之前说过的它与print @array.”/n”;的区别
由于print 需要一些输出的字符串列表,则其参数是作为列表context 来求值的。由于<>操作返回的是列表,则它们可很好
的一起工作:
print <>; # ‘cat’的源程序
print sort <>; #‘sort’的源程序
print 的括号在不影响语义的情况下是可选的
print (“Hello, world!/n”); #真实语义是将print作为函数调用
pirnt “Hello, world!/n” #真实语义是将print后面的所有东西当作列表打印
所以:print (2+3)*4; #oops!结果是打印了5,丢了运行结果4,因为此时print是系统调用,相当于(print (2+3)) * 4;
1.5 使用printf 格式化输出
printf “Hello, %s; your password expires in %d days!/n”,
$user, $days_to_die;
print “in %d days!/n”,17.85; #in 17 days!
printf “%6f/n”, 42; #输出为○○○○42(○此处指代空格)
printf “%23/n”,2e3+1.95; #2001
printf “%10s/n”, “wilma”; #输出为:○○○○○wilma
print “%-15s/n”, “flintstone”; #输出为flintstone○○○○
%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
下面一个例子好好理解printf和print的区别
#!/usr/bin/perl
printf "i want to see%%dlegend/n",23; #输出%的正确方法
printf "i want to see/%dlegend/n",23;#别以为和c语言一样,此时应该特别注意
printf "i want to see %d and %s/n",23;#内容不够,则省
printf "i want to see %d and %s/n",23,43,212;#格式符不够则省
结果:
legend@ubuntu:~/data/study/perl$ perl 5.5
i want to see%dlegend
i want to see23legend
i want to see 23 and
i want to see 23 and 43
1.5.1.数组和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;
printf“The items are:/n”. (“%10s/n”x @items), @items;
1.6 句柄
Perl 自身有六个文件句柄:STDIN,STDOUT,STDERR,DATA,ARGV,ARGVOUT
$ ./your_program <dino >Wilma
上述命令告诉shell,从一个名叫dino 读入,将结果输出到叫做wilma 的文件之中。由于程序从STDIN 读入,处理它(按照我们的要求),再输出到STDOUT,上述代码将能很好工作。
$ cat fred barney | sort | ./your_program | grep something | lpr
1.7 文件句柄的打开
open CONFIG, “dino”;
open CONFIG, “<dino”; #和上者一样,因为<是可以缺省的
open BEDROCK, “>fred”;#把文件fred当作标准输出
open LOG,“>>logfile”;#不是新建一个文件或者清空这个文件,而是从后面追加
可以在文件名的地方使用任何的标量表达式,虽然通常你可能不想这样做:
my $selected_output = “my_output”;
open LOG,“> $selected_output” #当$selected_output 为“>passwd”,则它将
变成追加。
Perl 的新版本中(从Perl5.6 开始),open 支持“3 参数”类型:
open CONFIG, “<”, “dino”;
open BEDROCK, “>”, $file_name;
open LOG, “>>”, &logfile_name();
这种方法的优点是Perl 不会将模式(第二个参数)和文件的名字(第三个参数)混淆,这能增加安全性
1.7.1.Bad 文件句柄
如果从一个bad 文件句柄读入(文件句柄没有恰当的打开),会立刻到达文件结尾(end-of-file)。如果写到一个bad 文件句柄,数据会被悄悄地丢掉。
如何判断文件句柄是否被正确地打开?
-w可以给予警告
也可以用open调用的返回值判断
my $success = open LOG, “>>logifle”; #将返回值保存在$success 中
if(!$success){
#打开失败时
...
}
1.7.2.关闭文件句柄
close BEDROCK;
当关闭文件句柄时,Perl 将告诉操作系统已经结束了对此数据流的操作,因此应当将输出的数据写到磁盘中,如果程序重新打开它(也就是说,使用open 重新打开此文件句柄),或者退出程序,Perl 将自动关闭文件句柄。
如果希望程序更加整洁,则每一个open 都应当使用一个close。通常,最好在不使用一个文件句柄时就立刻将它关闭,无论程序是否立即结束
1.8 严重错误和die
die 函数将打印出你给它的消息(利用标准错误流),并确保程序退出时为非零(nonzero)的退出状态(exit status)
if(!open LOG, “>>logfile”){
die “Cannot create logfile:$!”;
}
如果open 失败,则die 将结束程序,通常,当系统拒绝了我们的请求(如打开文件),$!将告诉你原因(可能是“权限不够(permission denied)”或者“(文件不存在)file not found”,针对本例)
也可以不用打印系统输出,因为有时根本就没有系统错误输出
if(@ARGV < 2){
die “Not enough arguments/n”;
}
1.9 警告信息和warn
warn 函数像die 那样工作,除了最后一步,它不会从程序中退出。
1.10 使用文件句柄
读入:
if(! open PASSWD, “/etc/passwd”){
die “How did you get logged in?($!)”;
}
while(<PASSWD>){
chomp;
...
}
输出:
写出(>)或追加的(>>)的文件句柄,可以和print 或printf 结合使用,如:
print LOG “Captain’s log, stardate 3.14159/n”; #输出到LOG 中
printf STDERR “%D percent complete./n”, $done/$total * 100;
printf (STDERR “%d percent complete./n”, $done/$total * 100);
printf STDERR (“%d percent complete./n”, $done/$total * 100);
都是正确的
1.10.1. 改变默认的输出句柄
默认情况下,如果不指定文件句柄给print(或者printf,这里的内容对两者均适用),则默认会使用STDOUT。但这个默认属性,可以通过select 操作进行更改。如下:
select BEDROCK;
print “I hope Mr. Slate doesn’t find out about this./n”;
print “Wilma!/n”
以后的print都受影响,因此要及时恢复回去,用select STDOUT;
$| = 1; #don’t keep LOG entries sitting in the buffer,设置它为1可以让print语句执行之后立即将文件句柄的内容写入文件
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.11 重新打开文件句柄
if(! Open STDERR, “>>/home/barney/.error_log”){
die “Can’t open error log for append: $!”;
}
因为STDERR默认打开的,所以立即关闭了原有的STDERR,而用新的日志文件替换了