Perl笔记2

21 输入与输出
(1) 读取标准输入
<STDIN>
chomp($line = <STDIN>);
while (defined($line = <STDIN>))
{
    print "I saw $line\n";
}
注:如果读取到文件结尾,行输入操作符就会返回undef。这样的设计是为了
配合循环使用,可以自然地跳出循环
上面的简写:
while (<STDIN>)
{
    print "I saw $line\n";
}

注:
#!/usr/bin/env perl
while (defined($line = <STDIN>))
{
    chomp($line); #chomp一般需要放在循环的第一行,而不是放置在循环条件中
    print "I saw $line\n";
}
输入CTRL+D时,会自动退出。
但是如果代码如下:
#!/usr/bin/env perl
while (defined(chomp($line = <STDIN>)))
{
    print "I saw $line\n";
}

则CTRL+D并不是退出

如果是在列表上下文中调用行输入操作符,它会返回一个列表,其中包含(其余)所有输入的输入内容
每个列表元素代表一行输入内容:
foreach (<STDIN>)
{
    chomp($_);
    print "I saw $_\n";
}

测试结果:
cat dog pig #输入再换行
1 2 3 #继续输入
"hello" "world" #继续输入,然后换行再输入CTRL+D
I saw cat dog pig #输出
I saw 1 2 3 #输出
I saw "hello" "world" #输出

请注意foreach与while循环的区别,如下为while循环:
#!/usr/bin/env perl
while (<STDIN>)
{
    chomp($_);
    print "I saw $_\n";
}
测试结果:
cat dog pig #输入后换行
I saw cat dog pig #输出
1 2 3 #再输入
I saw 1 2 3 #输出
"hello" "world" #再输入
I saw "hello" "world" #输出
可以看到while循环里面,Perl会读取一行输入,把它存入某个变量并且执行循环主体,接下来
它会回头去寻找其他的输入行。

注:如果读取一个400M的内容,foreach会一次性读入再输出,而while会每读取一行输出一行。最好使用while循环的简写,让它每次处理一行。

(2) 来自钻石操作符的输入
钻石操作符是行输入操作符的特例。不过它并不是从键盘取得输入,而是从用户指定的位置读取:
while (defined($line = <>))
{
    chomp($line);
    print "It was $line what I saw!\n";
}
测试如下:
[root@etl10 scott]# cat hello.pl
dog
cat
pig
[root@etl10 scott]# perl input.pl hello.pl
It was dog what I saw!
It was cat what I saw!
It was pig what I saw!

上面的程序可以简写成如下代码:
while (<>)
{
    chomp; #chomp不加参数时,直接作用于$_上
    print "It was $_ what I saw!\n";
}

(3) 调用参数
@ARGV数组保存调用参数
只要尚未使用钻石操作符,就可以对@ARGV动手脚
@ARGV = qw# larry moe curly #; #强制让钻石操作符只读取这三个文件,而不管调用参数是什么
while (<>)
{
    chomp;
    print "I was $_ what I saw!\n";
}

测试对@ARGV数组的修改:
[root@etl10 scott]# cat hello1
jiang
[root@etl10 scott]# cat hello2
shouzhuang
[root@etl10 scott]# cat hello.pl
dog
cat
pig
[root@etl10 scott]# cat input.pl
#!/usr/bin/env perl
@ARGV = qw# hello1 hello2 #; #强制让钻石操作符只读取这三个文件,而不管调用参数是什么
while (<>)   #读取的是@ARGV数组保存的文件列表
{
    chomp;
    print "I was $_ what I saw!\n";
}

[root@etl10 scott]# perl input.pl hello.pl
I was jiang what I saw!
I was shouzhuang what I saw!


(4) 输出到标准输出
print函数后面的()在一般情况下都是可以省略的
print函数的返回值不是真就是假,真是1。

(5) 使用printf函数格式化输出
#!/usr/bin/env perl
my $user = "hello";
my $expire_days = 3;
printf "Hello %s, your password expires in %d days!\n",$user,$expire_days;

测试输出结果:
Hello hello, your password expires in 3 days!

要输出恰当的数字形式,可以使用%g,它会按照需要自动选择浮点数、整数甚至是指数形式:
printf "%g %g %g\n",5/2, 51/17, 51 ** 17;
输出:
2.5 3 1.0683e+29

%d格式代表十进制整数,它会舍去小数点之后的数字
注意:它会无条件截图,而非四舍五入
printf "%6d\n", 42;
----42 #-代表空格
printf "%-6d\n", 42;
42----  #-代表空格
%s代表字符串格式

printf "%12s\n", "hello";
-------hello #-代表空格
printf "%12s\n", "hello";
hello------- #-代表空格

%f 转换格式(浮点数)会按照需要四舍五入,甚至还可以指定小数点之后的输出位数:
printf "%12f\n", 6*7 + 2/3;
---42.666667 #-代表空格
printf "%12.3f\n", 6*7 + 2/3; #3位小数
------42.667 #-代表空格      
printf "%12.0f\n", 6*7 + 2/3; #没有小数位
----------43 #-代表空格
printf "%%12f\n", 6*7 + 2/3; #要输出%,请使用%%
%12f

数组和printf
@items = qw/ hello1 hello2 hello3 /;
printf "The items are:\n" . ("%10s\n" x @items), @items;

测试结果:
[root@etl10 scott]# perl output.pl
The items are:
    hello1 #前面空四个空格
    hello2
    hello3
   
(6) 文件句柄
文件句柄就是程序里面代表Perl进程和外界之间的"I/O"联系的名称
建议使用全大写来命名文件句柄
但是有6个特殊文件句柄是Perl保留的。它们是:STDIN, STDOUT, STDERR, DATA, ARGV, ARGVOUT

一:打开文件句柄
open CONFIG, 'dino'; #打开名为CONFIG的文件句柄,让它指向文件dino
open CONFIG, '<dino';#同上,只是其指定此文件只是用来读取的,而非写入
open BEDROCK, '>fred'; #创建一个新的文件
open LOG, '>>logfile'; #追加的方式打开文件

查看Perl能理解和处理的字符编码清单:
perl -MEncode -le "print for Encode->encodings(':all')"
7bit-jis
AdobeStandardEncoding
AdobeSymbol
AdobeZdingbat
ascii
......
UCS-2BE
UCS-2LE
UTF-16
UTF-16BE
UTF-16LE
UTF-32
UTF-32BE
UTF-32LE
UTF-7
utf-8-strict
utf8
viscii

DOS风格的换行符(\r\n,即CR-LF风格)
而Unix风格的换行符则只有一个"\n",如果在DOS操作Unix文件时,需要确保得到的文件每行都是以
CR-LF结尾,就得在操作文件时使用这个特殊层:
open BEDROCK, '>:crlf', $file_name;
注意:如果原来就是CR-LF风格的话,转换后会多出一个换行符

读取DOS风格的文件时:
open BEDROCK, '<:crlf', $file_name;
读取文件的同时,Perl会把所有CR-LF都转换为Unix风格的换行符

以二进制方式读写文件句柄
关闭binmode对换行符相关的处理:
binmode STDOUT; #不要转换换行符
binmode STDERR; #不要转换换行符

如果希望输出Unicode到STDOUT,就要确保STDOUT知道如何处理它拿到的数据:
binmode STDOUT, ':encoding(UTF-8)';

有问题的文件句柄如何避免:
第一种方法:启动warnings; use warnings;
第二种方法:对open的返回值进行判断
my $success = open LOG, '>>', 'logfile';
if (! $success)
{
    #open操作失败
    ......
}

二:关闭文件句柄
close BEDROCK;

三:用die处理致命错误
if (! open LOG, '>>', 'logfile')
{
    die "Cannot create logfile: $!";
}
$!里面存放的是系统错误信息,同时会显示文件名和行号
如果不想显示系统错误信息,文件名和行号,可以如下设计:
if (! open LOG, '>>', 'logfile')
{
    die "Cannot create logfile!\n";
}

 
四:使用warn发出警告信息
 
五:自动检测致命错误:
从Perl 5.10开始,autodie编译指令已经成为标准库的一部分
use autodie; #如果open失败,它会自动启动die
open LOG, '>>', 'logfile';

测试1:
#!/usr/bin/env perl
use autodie;
open LOG, '>>', '/root/logfile';
测试结果:
Can't open '/root/logfile' for appending: 'Permission denied' at autodie.pl line 3

测试2:
#!/usr/bin/env perl
if (! open LOG, '>>', '/root/logfile')
{
    die "open LOG failed:$!";
}
测试结果:
open LOG failed:Permission denied at autodie.pl line 4.

六:使用文件句柄
测试1:
#!/usr/bin/env perl
if(! open PASSWD,"/etc/passwd")
{
    die "How did you get logged in? ($!)";
}
while (<PASSWD>)
{
    #chomp;
    print $_;
}

close PASSWD;

输出结果为:
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
......

测试2:
#!/usr/bin/env perl
$done = 10;
$total = 100;
open LOG,'>>',"hello.log";
print LOG "Captain's log, stardate 3.14159\n";
printf LOG "%d%% percent complete.\n",$done/$total * 100;

测试结果:
[root@etl10 scott]# cat hello.log
Captain's log, stardate 3.14159
10% percent complete.

七:改变默认的文件句柄
测试3:
#!/usr/bin/env perl
select LOG; #输出用的默认文件句柄
$done = 10;
$total = 100;
open LOG,'>>',"hello.log";
print "Captain's log, stardate 3.14159\n"; #不用指定文件句柄
printf "%d%% percent complete.\n",$done/$total * 100; #不用指定文件句柄
$| = 1;将默认文件句柄在每次进行输出操作后立刻刷新缓冲区。

测试结果:
[root@etl10 scott]# cat hello.log
Captain's log, stardate 3.14159
10% percent complete.

八:重新打开标准文件句柄
#将错误信息写到错误日志中,而不是默认的输出到屏幕
if (! open STDERR, ">>/home/test/.error_log")
{
    die "Can't open error log for append: $!";
}
注:重新打开STDERR之后,任何从Perl产生的错误信息都会送到新的文件里面。但是如果程序执行到die代码时,
错误信息会流到哪里?
在重新打开三个系统文件句柄STDIN,STDOUT,STDERR失败时,Perl会找回原先的文件句柄。


九:用say来输出
Perl 5.10从Perl 6中借来了say函数
在Perl 5.10中加入了一个新特性,就是可以为say指定一个文件句柄
use 5.010;
open BEDROCK,">> hello.log";
say BEDROCK "Hello!";

十:标量变量中的文件句柄
使用变量代替裸着文件句柄

#!/usr/bin/env perl
use 5.010;
open my $hello_fh, '>>', 'rocks.txt' or die "Could not open rocks.txt: $!"; #变量代替裸子文件句柄
foreach my $rock (qw/ hello1 hello2 hello3 /)
{
    say $hello_fh $rock;
}

print $hello_fh "limestone\n";
print $hello_fh; #错误,这样会打印GLOB(0x6abd48)
close $hello_fh;

总结:
一般短小的程序,使用裸字也OK
不过对于大一点的项目和应用程序来说,使用标量变量的方式,可以精确控制文件句柄的作用域,方便调试和维护。

例子1:
#!/usr/bin/env perl
print reverse <>; # 将执行中调用的文件的内容逆序打印出来 同linux系统的tac命令相似

测试结果:
[root@etl10 scott]# perl tac.pl 1 2 3
cat
kitty
4
3
2
world
Hello
[root@etl10 scott]# cat 1 2 3
Hello
world
2
3
4
kitty
cat

例子2:
#!/usr/bin/env perl
print "Enter some lines, then press Ctrl-D:\n";
chomp(my @lines = <STDIN>);
print "1234567890" x 7, "12345\n";
foreach (@lines)
{
    printf "%20s\n", $_;
}

测试结果:
[root@etl10 scott]# perl output.pl
Enter some lines, then press Ctrl-D:
noas
website
www.csdn.net
www.bill-jc.com
123456789012345678901234567890123456789012345678901234567890123456789012345
                noas
             website
        www.csdn.net
     www.bill-jc.com
    
上面的foreach也可以替换为:
my $format = "%20s\n" x @lines;
printf $format, @lines;

注:如果将上面的代码修改为:
#!/usr/bin/env perl
print "Enter some lines, then press Ctrl-D:\n";
my @lines = <STDIN>;
print "1234567890" x 7, "12345\n";
my $format = "%20s" x @lines;
printf $format, @lines;

则经测试,会发现每行少一个字符:
[root@etl10 scott]# perl output.pl
Enter some lines, then press Ctrl-D:
noas
website
www.csdn.net
www.bill-jc.com
123456789012345678901234567890123456789012345678901234567890123456789012345
               noas
            website
       www.csdn.net
    www.bill-jc.com
原因是统计20个字符时包含换行符。

如下验证过程: 
#!/usr/bin/env perl
$line = <STDIN>;
my $line_length = length($line);
print "$line_length\n";
测试,结果会比输入的字符数多一位:
[root@etl10 scott]# perl output.pl
hello #后面包含了\n换行符
6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值