Perl读取标准输入<STDIN>、读取文件输入<>和chomp函数
读取标准输入<STDIN>
<STDIN>
表示从标准输入中读取内容,如果没有,则等待输入。<STDIN>
读取到的结果中,如果没有意外,都会自带换行符。
例如,test.plx文件内容:
#!/usr/bin/perl
#
$line=<STDIN>;
if($line eq "\n"){
print "blank line\n";
} else {
print "not blank: $line"
}
注意上面的else语句中,$line
后面没有加换行符,因为<STDIN>
自带换行符。
下面的命令,将等待输入和回车。如果直接回车,则if条件为真。
perl test.plx
下面是和bash shell交互。
echo "hello" | perl test.plx
echo -e "haha\nheihei" | perl test.plx
注意上面第二条语句中,heihei会被忽略,因为上面的操作是标量上下文(上下文的概念,以后会解释),在发现换行符的时候,结束输入的读取,所以看到haha后面的"\n"就结束了。
因为<STDIN>
读取的是标准输入,所以如果要通过它读取文件内容,需要使用shell的重定向功能。例如,读取a.txt文件的内容到<STDIN>
:
perl test.plx <a.txt
另外,<STDIN>
在标量上下文中返回的是某一行,可以使用while来遍历多行,但在遍历时要书写准确:
# 错误遍历
$line = <STDIN>;
while(defined($line)){
print $line;
}
# 正确遍历
while(defined($line = <STDIN>)){
print $line;
}
第一种写法会无限循环输出读取的第一行(\n
前面的行),因为$line
被赋值为该第一行,且不再改变,由于defined()
返回真,会使得while无限循环。
第二种写法每次读取一行,每读取一行做下读取的位置标记方便下次读取(也就是说<STDIN>
是可迭代对象),直到读取完最后一行返回undef使得defined返回false,结束循环。
由于<STDIN>
在读取到最后一行后会返回undef,所以可以简写上面的第二种方式:
# (建议写法)
while(<STDIN>){
print $_;
}
# 或(不建议写法)
foreach(<STDIN>){
print $_;
}
只是需要注意上面的两种简写方式,每次读取的行并不是直接赋值给$_
,而是在每次迭代过程中赋值的。换句话说,上面的<STDIN>
和$_
没有直接关系,不像$line=<STDIN>
这种赋值,直接在读取的时候就赋值给$line,也正因为不是直接赋值给变量,才能循环读取下去。
(本段涉及到上下文和数组的概念,暂时还没介绍,如不理解,可先略过)上面的while和foreach有巨大的差别,while后面是标量上下文,foreach后面是列表上下文。这意味着while是每次从文件中读取一行,打上位置标记以便下次读取,然后进入循环体。而foreach因为操作目标是列表,它会一次性将文件中所有行读取到内存,然后当作列表进行遍历,所以性能非常差,例如400M的文件,也许需要1G的内存,因为perl会预估先分配足够多的内存以便后续不再因为分配内存的事而中断进程。上面的过程,使用下面的形式描述,就很容易理解了:
$line = <STDIN>; # 一次读一行,性能好
@lines = <STDIN>; # 一次读所有,性能差
<STDIN>
会带有换行符,通常都会加上chomp()
操作符去掉换行符,关于chomp,见下文。
读取文件输入<>
perl中使用两个尖括号符号表示读取来自文件的输入,例如从命令行中传递文件作为输入源。这个符号被称为"钻石操作符"。
例如,test.plx程序内容如下:
#!/usr/bin/perl
while(defined($line = <>)){
print $_;
}
或者简写的:
#!/usr/bin/perl
while(<>){
print $_;
}
然后在perl程序的命令行中指定输入文件:
$ ./test.plx test.log
或者使用@ARGV
数组指定输入文件:
#!/usr/bin/perl
@ARGV=qw(test.log);
如果想要读取标准输入的数据,则在命令行中使用短横线-
。
$ echo -e "haha\nheihei" | ./test.plx -
一般来说,while循环中使用<STDIN>
或<>
读取输入后(也包括open关键字打开文件再读取行的情况),第一行就是去除行尾的换行符,所以大多数都采用如下通用格式:
while(<>){
chomp;
COMMANDS;
}
while(<STDIN>){
chomp;
COMMANDS;
}
上面的chomp没有参数,所以采用默认变量$_
,也就是迭代中的每一行。
关于<>
的机制,请继续阅读下文的<<>>
。
chomp()函数
这个函数用于去掉字符串或者读取到的标准输入的一个 换行符。注意关键字:字符串、一个、换行符。
注意,这个函数是在原处修改字符串。但这个函数有自己的返回值:
- 如果能去掉换行符,则返回移除的字符数,也就是数值1。这是个没什么用的返回值,因为我们都已经知道了;
- 如果没有换行符,则返回数值0;
- 如果结尾有两个换行符,则只去掉一个。
比较下面两个程序,它们会从标准输入中读取:
$line=<STDIN>;
print $line;
$line=<STDIN>;
chomp($line);
print $line;
或者下面的例子,它直接操作一个已有的字符串:
$foo="hello world!\n"
chomp($foo);
print $foo;
字符串赋值操作和chomp()函数可以结合在一起。
chomp($line=<STDIN>);
chomp($line="hello world!\n");
print $foo;
但注意,chomp()是有返回值的。下面返回的是数值1。
print chomp($line="hello world!\n");
<<>>
实际上,除了<>
还有<<>>
。它们之间有区别,目前为止还没有介绍文件句柄和ARGV变量,所以能看懂则看,看不懂则过。
<>
会隐式打开来自@ARGV
数组中的参数文件,打开方式是两参数格式的open,类似于open "FH","$ARGV[N]"
。正常情况下这没什么问题,但可能会成致命的危险。例如Perl脚本文件a.pl内容如下:
#!/usr/bin/perl
while(<>){print}
如果执行该脚本的方式为:
$ perl a.pl "rm -rfv * |"
其中rm -rfv * |
被当作一个参数收集到@ARGV
数组中,因为<>
以两参数模式的方式隐式打开文件,这等价于:
open FH,"rm -rfv *|";
这表示先打开一个管道,再执行rm -rfv *
命令,并将该命令的输出通过管道传递供<>
读取。
再看下面的示例,它们是等价的。
$ perl a.pl "ls /tmp |"
$ ls /tmp | perl a.pl
这在写一行式perl程序的时候比较方便:
perl -pe '' "ls /tmp |"
ls /tmp | perl -pe ''
所以,以<>
的方式打开@ARGV
中的文件时,因为无法保证这个数组中的参数一定是文件,所以可能会出现危险操作。
而<<>>
则能避免这个问题,它隐式地以三参数的open打开@ARGV
中的文件。类似于:
open FH,"<","$ARGV[N]";
它限定了第二个参数是输入重定向操作,所以它保证了@ARGV
中的参数必须是文件,否则就会报错。
perl -e 'while(<<>>){print}' /etc/passwd
perl -e 'while(<<>>){print}' "ls /tmp |" # 报错
这时想要从管道读取数据,只能将管道放在perl命令的前面。
ls /tmp | perl -e 'while(<<>>){print}'
Perl读取标准输入<STDIN>、读取文件输入<>和chomp函数的更多相关文章
- 【Linux 应用编程】文件IO操作 - 常用函数
Linux 系统中的各种输入输出,设计为"一切皆文件".各种各样的IO统一用文件形式访问. 文件类型及基本操作 Linux 系统的大部分系统资源都以文件形式提供给用户读写.这些文件 ...
- python通过标准输入读取内容,读取键盘输入的内容?接收用户输入?
需求说明: 在交互式脚本中,需要用户手动输入内容,并对内容进行处理.在这里记录下通过 python的内置函数input()读取标注输入的内容.默认的标准输入是键盘. 操作过程: 1.通过input() ...
- qemu:///system 没有连接驱动器可用;读取数据时进入文件终点: 输入/输出错误
原因 1. KVM的相关包 装少了 2KVM的相关包 重新安装 3 May 31 15:22:55 localhost libvirtd: 2019-05-31 07:22:55.554+0000: ...
- Go基础系列:读取标准输入
fmt包中提供了3类读取输入的函数: Scan家族:从标准输入os.Stdin中读取数据,包括Scan().Scanf().Scanln() SScan家族:从字符串中读取数据,包括Sscan().S ...
- [Xcode 实际操作]七、文件与数据-(8 )读取和解析Plist文件(属性列表文件)
目录:[Swift]Xcode实际操作 本文将演示如何读取和解析Plist文件,即属性列表文件. 它是用来存储,串行化后的对象的文件. 在项目名称上点击鼠标右键,弹出右键菜单, 选择[New File ...
- python读取txt批量创建文件
python读取txt批量创建文件 pythonbatchfile 前几天有个小问题, 需要批量建立很多文件夹,, 所以手动写了个小的脚本, 后续可以直接使用 读取目录文件, 然后直接创建相应的文件 ...
- Java——读取和写入txt文件
package com.java.test.a; import java.io.BufferedReader; import java.io.BufferedWriter; import java.i ...
- Android想服务器传图片,透过流的方式。还有读取服务器图片(文件),也通过流的方式。
/** * Created by Administrator on 2016/7/19. */ import android.util.Log; import com.gtercn.asPolice. ...
- Java读取Level-1行情dbf文件极致优化(3)
最近架构一个项目,实现行情的接入和分发,需要达到极致的低时延特性,这对于证券系统是非常重要的.接入的行情源是可以配置,既可以是Level-1,也可以是Level-2或其他第三方的源.虽然Level-1 ...
随机推荐
- c刷题
1.转义字符: C中定义了一些字母前加 "\" 来表示常见的那些不能显示的ASCII字符,如\0 空字符,\r 回车, \n换行等,就称为转义字符,因为后面的字符,都不是它本来的A ...
- ppt演讲者模式
步骤如下: 1 win+p 2 选择扩展 3 选幻灯片放映 4 设置幻灯片放映 5 显示器二 6 勾选显示演示者视图
- socketserver 实现并发
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环 socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题) server类: reque ...
- Python TypeError: 'module' object is not callable 原因分析
今天尝试使用pprint进行输出,语句为 >>>import pprint >>>pprint(people) 结果报错,TypeError: 'module' o ...
- 使用django我的第一个简单项目流程
项目概述:本项目实现的是员工提交需要审批的事情给老板(例如请假事件.某些具体事务需要老板确认事件等),老板确认或者拒绝该事件,员工登录员工自己的页面可以查询响应的状态信息. 代码实现概略:需要创建两个 ...
- Gitee(码云)、Github同时配置ssh key
一.cd ~/.ssh 二.通过下面的命令,依次生成两个平台的key $ ssh-keygen -t rsa -C "xxxxxxx@qq.com" -f "github ...
- Codeforces831D Office Keys
D. Office Keys time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...
- spak数据倾斜解决方案
数据倾斜解决方案 数据倾斜的解决,跟之前讲解的性能调优,有一点异曲同工之妙. 性能调优中最有效最直接最简单的方式就是加资源加并行度,并注意RDD架构(复用同一个RDD,加上cache缓存).相对于前面 ...
- 点击a标签的文字后页面的跳转
1.方法一 (1)js var html=""; html+="<a href=\"#\" onclick=\check('"+id+ ...
- @ResponseBody 返回乱码 的解决办法
1:最快的 最简单的办法是 在Ajax请求脸面指定头信息Accept属性,StringHttpMessageConverter默认iso-8859-1编码,但是会根据请求头信息指定的编码格式来转换 ...