Perl Learning (5) —— 输入与输出

(1) 读取标准输入
(2) 钻石操作符输入
(3) 调用参数
(4) 输出到标准输出
(5) 使用printf格式化输出
(6) 数组和printf
(7) 文件句柄
(8) 打开和关闭文件句柄
(9) 用die处理严重错误
(10) 使用warn输出警告信息
(11) 使用文件句柄
(12) 改变默认的文件输出句柄
(13) 使用say来输出


(1) 读取标准输入
“行输入”<STDIN>操作符
【1】在标量上下文中
调用“行输入”操作符,将会返回标准输入中的一行。
$line = <STDIN>;    # 读取下一行
chomp($line);    # 截掉最后的换行符
简写:
chomp($line = <STDIN>);# 习惯用法,效果同上
注意:如果读到文件结尾(end-of-file),“行输入”操作符就会返回undef —— 这样的设计是为了配合循环使用,可以自然地跳出循环。

  1. #!/usr/bin/perl  
  2. while (defined($line = <STDIN>)) {  
  3.     print "I saw $line";  
  4. }  
  5. #简写:  
  6. #!/usr/bin/perl  
  7. while (<STDIN>) {# 条件简写  
  8.     print "I saw $_";# 老地方$_  
  9. }  

注意:
[1] 只有当while循环的条件表达式里只有“行输入”操作符的前提下,这个简写才起作用。假如条件表达式里放了其他的东西,它就无法按你的预期运行了。
[2] “行输入”操作符(<STDIN>)和Perl的“老地方”变量($_)之间并没有什么关联。只是在这个简写里,输入的内容会恰好存储在$_变量中而已。
【2】在列表上下文中
调用“行输入”操作符,它会返回一个列表,其中包含(其余)所有的输入内容,每个列表的元素代表一行输入内容。

  1. #!/usr/bin/perl  
  2. #while (<STDIN>) {# 简写  
  3. #   print "I saw $_";# 老地方S_  
  4. #}  
  5. foreach (<STDIN>) {  
  6.     print "I saw $_";  
  7. }  

注意:while循环和foreach循环的区别
两者不同之处在于它们背后的运作方式。
在while循环里,Perl会读取一行输入,把它存入某个变量并且执行循环的主体。接下来,它会回头去寻找其他的输入行。
在foreach循环里,“行输入”操作符会在列表上下文中执行(因为foreach需要逐项处理列表的内容)。为此,在循环能够开始执行之前,它必须先将输入全部读进来。
建议:最好的做法,对于大文件,通常是尽量使用while循环的简写,让它每次处理一行。

(2) 钻石操作符输入
还有另一种读取输入的方法,就是使用钻石操作符<>。
它能让程序在处理调用参数的时候,提供类似于标准Unix工具程序的功能。
程序的调用参数(invocation arguments)通常是命令行上跟在程序名后面的几个“单词”。
注意:
[1] 连字符(-)当作参数,代表标准输入。
[2] 让程序以这种方式运行的好处,就是你可以在运行时指定程序的输入源。

  1. #!/usr/bin/perl  
  2. #while (<STDIN>) {# 简写  
  3. #   print "I saw $_";# 老地方S_  
  4. #}  
  5. #foreach (<STDIN>) {  
  6. #   print "I saw $_";  
  7. #}  
  8. while (defined($line = <>)) {  
  9.     chomp($line);  
  10.     print "It was $line that I saw!/n";  
  11. }  
  12. #简写:  
  13. while (<>) {  
  14.     chomp;# 使用chomp的默认用法:不加参数时,chomp会直接作用在$_上。  
  15.     print "It was $_ that I saw!/n";  
  16. }  


注意:
[1] 钻石操作符是“行输入”操作符的特例。不过它并不是从键盘取得输入,而是从用户指定的位置读取。
[2] 钻石操作符只有在碰到所有输入的结尾时,才会返回undef。(然后就会跳出while循环)
[3] 由于钻石操作符通常会处理所有的输入,所以当它在程序里出现好几次时,通常是错误的。

(3) 调用参数
钻石操作符其实不会去检查调用参数,它的参数其实是来自@ARGV数组。
注意:
[1] 这个数组是由Perl解释器事先建立的特殊数组,其内容就是由调用参数组成的列表。在程序开始运行时,@ARGV里就已经塞满了调用参数。
[2] 如果@ARGV是空列表,就会改用标准输入流。
[3] 只要尚未使用钻石操作符,你就可以对@ARGV动点手脚。

  1. foreach (@ARGV) {  
  2.     print "$_/n";  
  3. }  
  4. @ARGV = qw# filename #;# 强制让钻石操作符读取我们指定的文件  
  5. while (<>) {  
  6.     chomp;  
  7.     print "It was $_ that I saw!/n";  
  8. }  

 

(4) 输出到标准输出
print操作符会读取一个列表里的所有值,并把每一项依次送到标准输出。
print @array;# 元素之间没有空格
print "@array";# 数组所有元素之间用空格分开
注意:因为Perl把数组内插到字符串中时,会在每个元素之间加上空格。
由于print处理的是待打印的字符串列表,因此它的参数会在列表上下文中执行。而钻石操作符(“行输入”操作符的特殊形式)在列表上下文中会返回由许多输入行组成的列表,所以它们彼此可以配合工作。
print <>;# 和Unix下的'cat'命令功能差不多
print sort <>;# 和Unix下的'sort'命令功能差不多
注:现在你可以用Perl重写所有的Unix工具程序。
注意:print后面的括号可有可无 —— 除非这样做会改变表达式的意义,否则Perl里的括号可以省略。
print (2+3)*4;# 输出5
print ( (2+3)*4 )# 输出20

(5) 使用printf格式化输出

  1. $user = wcdj;  
  2. $days_to_die = 30;  
  3. printf "Hello, %s; your password expires in %d days!/n", $user, $days_to_die;  

常用的转换(conversion):

  1. %g —— 要输出恰当的数值形式,它会按需求自动选择浮点数、整数、指数形式  
  2. printf "%g %g %g/n"5/251/1751**17;# 2.5 3 1.0683e+29  
  3. %d —— 输出十进制的整数,它会舍去小数点之后的数字(注意:它会无条件舍去,而非四舍五入)  
  4. printf "in %d days!/n"17.85;# in 17 days!  
  5. %f —— 输出浮点数,会按需要四舍五入  
  6. printf "%12f/n"6*7+2/3;       # ^^^42.666667  
  7. printf "%12.3f/n"6*7+2/3;    # ^^^^^^42.667  
  8. printf "%12.0f/n"6*7+2/3;    # ^^^^^^^^^^43  
  9. %s —— 字符串内插  
  10. printf "%10s/n""wcdj";# ^^^^^^wcdj ("^"表示空格)  

注意:
[1] 对齐方向:负数(左对齐);正数(右对齐)。
[2] %%,不会输出(参数)列表中的任何元素。

(6) 数组和printf
动态产生格式字符串。

  1. my @items = qw( wcdj gerry yj );  
  2. my $format = "The items are:/n" . ("%10s/n" x @items);# x操作符  
  3. print "the format is >>$format<</n";# 用于调试  
  4. printf $format, @items;  

注意:在标量上下文中,用了一次@items以取得它的长度,然后又在列表上下文中用了它一次以取得它的内容。(上下文的重要性)

(7) 文件句柄
建议:使用全大写字母来命名文件句柄。
【六个特殊文件句柄】(Perl保留的)
STDIN、STDOUT、STDERR、DATA、ARGV和ARGVOUT。

(8) 打开和关闭文件句柄
Perl提供的三种文件句柄STDIN、STDOUT和STDERR,都是由产生Perl进程的父进程(可能是shell)自动打开的文件或设备。
当你需要其他的文件句柄时,请使用open操作符。
open CONFIG, "filename";
open CONFIG, "<filename";# 从文件中输入
open BEDROCK, ">filename";#输出到文件(“>”会重新创建文件)
open LOG, ">>logfile";#输出到文件(“>>”追加的方式)
在Perl 5.6之后,open另有一种使用三个参数的写法。
open CONFIG, "<", "filename";

不正确的文件句柄
open的返回值来判断句柄是否正确:
my $success = open LOG, ">>logfile";# 捕获返回值
if (! $success) {
    # open操作失败
    # ...
}

关闭文件句柄
当你不再需要某个文件句柄时,可以用close操作符来关闭它。
close CONFIG;
建议:请为每个open搭配一个close。最好是在每个文件句柄用完之后就立刻关闭它,哪怕程序马上就结束了。
注:
关闭文件句柄会:[1] 刷新输出缓冲 [2] 释放该文件上的任何锁。

(9) 用die处理严重错误
当Perl遇到严重错误(fatal error)时,程序应该终止运行,并用错误信息告知原因。
die函数会输出你指定的信息(到为这类信息预留的标准错误流中),并且让你的程序立刻终止,并返回不为零的退出码。
if (! open LOG, ">>logfile") {
    die "Cannot create logfile: $!";
}
注意:
[1] $! 代表:可读的系统出错信息。当系统拒绝我们所请求的服务时,$! 会给我们一个理由。—— 即,解释性的系统错误信息就保存在Perl的特殊变量$!中。
[2] 只有在系统服务请求失败后的瞬间,$! 的值才会有用。如果操作成功了,就不会在$!里留下任何有用的信息。
[3] die还会自动将Perl程序名和行号附加在错误信息的后面。

(10) 使用warn输出警告信息
warn函数和die函数的区别:不同之处在于最后一步,warn不会终止程序的运行。

(11) 使用文件句柄
当文件句柄以读取模式打开后,可以从它读取一行数据,就像从STDIN读取标准输入流一样。

  1. if (! open FILE, "<filename") {  
  2.     die "Cannot read file: $!";  
  3. }  
  4. while (<FILE>) {  
  5.     chomp;  
  6.     print "$_/n";  
  7. }  

写入或是添加模式打开的文件句柄可以在print或printf函数中使用。—— 使用时,请直接将它放在关键字之后、参数列表之前。

  1. if (! open LOG, ">>logfile") {  
  2.     die "Cannot create logfile: $!";  
  3. }  
  4. $name = "wcdj";  
  5. print LOG "2011-2-1/n";  
  6. printf LOG "My name is %s./n", $name;  

 

(12) 改变默认的文件输出句柄
默认情况下,假如不为print或是printf指定文件句柄,它的输出就会送到STDOUT。
不过,你可以使用select操作符来改变默认的文件句柄。

  1. if (! open LOG, ">>logfile") {  
  2.     die "Cannot create logfile: $!";  
  3. }  
  4. $name = "wcdj";  
  5. print "2011-2-1/n";  
  6. printf "My name is %s./n", $name;  
  7. select LOG;# 改变默认的文件句柄  
  8. print "select used/n";  
  9. print "2011-2-1/n";  
  10. printf "My name is %s./n", $name;  
  11. select STDOUT;# 恢复默认句柄  
  12. print "select used/n";  
  13. print "2011-2-1/n";  
  14. printf "My name is %s./n", $name;  

 

建议:当你所指定的默认文件句柄使用完毕之后,最好把它设回原先的默认值STDOUT来。
注意:将特殊变量$| 设定为1,就会使当前的默认文件句柄在每次进行输出操作后,立刻刷新缓冲区。

(13) 使用say来输出
Perl 5.10从正在开发的Perl 6 中借来了say这个函数。
它的功能和print函数差不多,但是会在每行输出的结尾自动加上换行符。
use 5.010;
say "Hello!";# 比print和/n省掉4次按键
等价于:
print "Hello/n";

posted @ 2011-10-27 10:20  xlhuang  阅读(424)  评论(1编辑  收藏  举报