Perl一行式:处理行号和单词数 (2)

也就是说,while循环体内不做任何操作,直到<>读取完成后,while结束,然后运行一次性语句块{print $.}。所以,-ne }{xxx等价于在END语句块中执行xxx操作。

模仿 wc -l

wc -l会单独输出每个文件的行数,并总计所有文件的行数。例如:

$ wc -l file.log 5 file.log $ wc -l file.log paragraph.log 5 file.log 18 paragraph.log 23 total

所以,这也可以使用perl一行式程序来实现。因为这个逻辑中需要单独统计每个文件,所以必须显式区分每个文件。使用eof判断每个文件的尾部即可。

$ perl -M'List::Util qw(sum)' -lne ' # 将@ARGV保存起来,以便后续能够按先后顺序获取所有文件名 BEGIN{ @files = @ARGV; } # 每个文件处理完时,保存行和文件信息,并关闭文件以便重置行号计数器 if(eof){ # 将每个文件的行数注册到一个hash结构中 # hash的key为当前处理的文件名 $line_filename{$ARGV} = $.; close ARGV; } END{ # 获取总行数以及总行数的字符长度,以便格式化对齐 $total_lines = sum values %line_filename; $longest = length $total_lines; # 输出每个文件对应的行数及文件名,且按照@ARGV的顺序输出 foreach (@files){ printf "%${longest}d %s\n",$line_filename{$_},$_; } # 输出总行数 print "$total_lines total"; } ' file.log paragraph.log

这是遇到的第一个比较大的程序,这样的逻辑应该写成Perl脚本而不是一行式程序。不过这里的几个知识点很适合引入Perl一行式。

先分析下这段程序的逻辑:要统计总行数,且要输出每个文件对应的行数,输出时还要进行格式化对齐,所以先将每个文件对应的行数保存到一个hash结构中,最后在END语句块中计算总行数,并计算总行数有多少个字符以便确定格式化对齐时的字符数量。

上面使用了-M选项,它表示导入一个模块。此处所导入的模块是List::Util模块,它是额外的列表(数组)工具模块,该模块中有不少处理列表的工具。例如这里使用qw(sum)表示导入这个模块中的sum函数,用于对列表元素进行加总。如果不写qw(sum),那么在使用sum函数的时候,需要写完整的名称List::Util::sum @arr。

"-l"选项的目的是给print函数追加换行符。

另外这里使用了BEGIN语句块来保存@ARGV数组,虽然%line_filename中也能取得所有的文件名,但hash结构中的元素是无序的,要保证文件名的顺序,只能使用数组(列表)来保存。再者,因为@ARGV中的参数文件会随着<>的读取而被剔除出@ARGV,所以应该在BEGIN中对@ARGV进行保存。

统计非空白行的行数

用计数器实现非常简单:

$ perl -lne '++$num if /\S/;END{print $num+0;}' paragraph.log

这里的逻辑非常简单。唯一需要注意的是$num+0,因为文件可能是空的,使得END语句块中的$num变量仍处于未定义状态。加上一个+0,可以保证它会输出数值格式,未定义时则输出0。

为了保证得到数值,可以使用int函数进行转换:

$ perl -lne '++$num if /\S/;END{print int $num;}' paragraph.log

我准备在这里引入grep函数的简单用法。

Shell中有个grep命令可以用来匹配内容,在Perl中也有一个grep函数,它的简单工作方式可以类同于shell的grep命令,用于筛选列表中符合条件的元素,并将这些元素构成一个新的列表。比如能正则匹配的元素、操作后布尔真的元素。

所以,可以使用grep函数来匹配非空白行:

$ perl -e '@lines = grep /\S/,<>;print "@lines"' paragraph.log

grep期待的是列表上下文,使得<>一次性读完所有行形成一个列表,然后grep对这个列表的每个元素进行筛选,只要是非空白行都放入一个新的列表。

那么要统计非空白行数就非常简单了,直接将grep的结果转换成标量上下文就可以。

$ perl -le 'print ~~grep /\S/,<>' paragraph.log

前面说过,~~可以用来转换标量上下文。

其实Perl grep函数要强大的多,它支持完整的流程控制逻辑。如有需要,参考Perl grep函数。

计数每个单词

为文件中每行中的单词进行计数。

$ perl -pe 's/(\w+)/"<".++$num.">.$1"/ge' file.log <1>.first <2>.paragraph: <3>.first <4>.line <5>.in <6>.1st <7>.paragraph <8>.second <9>.line <10>.in <11>.1st <12>.paragraph <13>.third <14>.line <15>.in <16>.1st <17>.paragraph

这里使用s///命令的e修饰符。该修饰符可以评估s/reg/replacement/的replacement部分,将其作为Perl的代码被perl执行,然后进行s替换操作。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysjfp.html