使用perl取得文件指定行
2013-04-03 11:08:51 阿炯

本站赞助商链接,请多关照。 用系统工具(head,tail)取得文件(日志)的第一、最后一行非常容易,如何使用perl来实现行呢,本文讨论一些实现的方法(Capturing the first and last line of a file)。

my $last_line;
while(<FILE>) {
 $last_line = $_ if eof;
}
print $last_line;

use File::ReadBackwards;
open(my $fh, "test") or die("ack - $!");
my $first = <$fh>;
close $fh
my $last  = File::ReadBackwards->new("test")->readline;

Use File::ReadBackwards module from CPAN.
For more info on the module and Performing a tail(1) in Perl (reading the last N lines of a file) for a good node on reading the last line of a file.

open (FILE, "test") || die "ERROR Unable to open test: $!\n";
my $first = <FILE>;
my $last = $first;
while (<FILE>) {$last = $_}
close FILE;
print "$first\n$last\n";

这里添加了文件只有一行时的处理方法。

Use Tie::File module from CPAN.
This module represents a text file as a Perl array and the file is not loaded into memory.(总体来说这个模块还是在这类需求上处理了相当好的,在新的版本中它已经是核心模块了)

tie @array, 'Tie::File', $fname or die "Can't tie $fname: $!";
my $first = shift (@array); # First line of the file
my $last = pop (@array); # Last line of the file
untie @array;

更简单的方法(打印首、末行):
print scalar <FILE>;
while (<FILE>) {print if eof}


perl -ne 'BEGIN {print scalar <>} print if eof' /path/file

得到perl内部解析执行过程:
perl -MO=Deparse -pe'exit if $.>2'

perl -ne 'print if eof()' file
perl -e 'while (<>) { if ($_) {$last = $_;} } print $last;' < my_file.txt
#取得两个文件的首行
perl -ne 'print "$ARGV : $_" if $. == 1; } continue { close ARGV if eof;' a  b
#取得两个文件的末行
perl -ne 'print "$ARGV : $_" if eof } continue { close ARGV if eof;' a b

或直接调用系统工具实现也许在某些情况下更好:

system head -1 $file; system tail -1 $file;
ps aux>ps
grep -A 1 -B 1 "sshd" ps
grep -B 1  "sshd$" ps

---------------
下面介绍一些关于perl文件操作

open( $input_fh, "<", $input_file ) || die "Can't open $input_file: $!";

在用完文件名柄,应该主动调用'close'将其关闭,当然在脚本退出时,它也自动关闭,当然这并不是负责的程序员所为了。

将整个文档装入数组中(如果文件很大时,你可要小心内存不足):
my @lines = <$input_fh>;

将文档所有行连在一起,组成一个字符串:
my $text = join('', <$input_fh>);

当文件很大时,应该使用'while'来代替'foreach'来对文件数组的处理,当循环比较大的数组的时候,建议使用while替代foreach,因为perl会为foreach的所有数组分配内存空间,而while则不会,所以当数组很大的时候,使用foreach会造成out of memory的情况发生,而这种错误很难被发现。

或者打开文件,在必要才载入部分进入内存。如下示例:
use autodie;
open(my $image_fh, '<', $filename);

或使用OO风格:
use FileHandle;

my $handle = FileHandle->new( "< $file_to_read" );
croak( "Could not open '$file_to_read'" ) unless $handle;
my $line1 = <$handle>;
my $line2 = $handle->getline;
my @lines = $handle->getlines;
$handle->close;

use IO::File;
my $file = shift @ARGV or die "what file?";
my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
my $data = do { local $/; <$fh> };
$fh->close();

# If you didn't just run out of memory, you have:
printf "%d characters (possibly bytes)\n", length($data);

使用'while'进行一行一行地处理:
my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
while (my $line=<$fh>) {
 print "Better than cat: $line";
}
$fh->close();

Read the entire file $file into variable $text with a single line:
读入整个文档,将所有行合为一行,赋给一变量:
$text = do {local(@ARGV, $/) = $file ; <>};

or as a function
$text = load_file(@ARGV);
sub load_file {local(@ARGV, $/) = @_; return <>}

---------------
There are two common ways to open a file depending on how would you like to handle error cases.

打开出错时退出
Case 1: Throw an exception if you cannot open the file:
my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file '$filename' $!";
while (my $row = <$fh>) {
 chomp $row;
 print "$row\n";
}

打开出错时只警告
Case 2: Give a warning if you cannot open the file, but keep running:
my $filename = 'data.txt';
if(open(my $fh, '<:encoding(UTF-8)', $filename)){
 while (my $row = <$fh>) {
  chomp $row;
  print "$row\n";
 }
}else{
 warn "Could not open file '$filename' $!";
}

用Perl直接创建一个UTF-8的文件
open( OUT, ">:utf8", "a.txt" ) or die "a.out: $!";
print OUT "\x{feff}";
print OUT "aaaa\n";
close OUT;

------------------------------
从文件的指定行读取内容
Reading the file using the line number


Reading a specific line from large file

Tie::File模块是最佳选择。

tie my @array, 'Tie::File', $file_name or die("Unable to open file \"$file_name\": $!\n");
print($array[$_]) for 6, 1003, 2965;

Tie::File can use a fair bit of memory (for its index, which is above and beyond what the memory argument limits), so you might want to write your own (faster, more memory efficient) solution.

my %lines_of_interest = map { $_ => 1 } 6, 1003, 2965;
open my $fh, '<', $file_name or die("Unable to open file \"$file_name\": $!\n");
my $num_lines = keys %lines_of_interest;
while (<$fh>){
   if($lines_of_interest{$.}){
      print;
      last unless --$num_lines;
   }
}

在命令行中的实现
perl -ne 'print if $. == $number; exit if $. > $number;'

再提供几个示例:

use IO::File;
my $file = shift @ARGV or die "what file?";
my $fh = IO::File->new( $file, '<' ) or die "$file: $!";
my $data = do { local $/; <$fh> };
$fh->close();

# If you didn't just run out of memory, you have:
printf "%d characters (possibly bytes)\n", length($data);

And when going line-by-line:
my $fh = IO::File->new($file, '<') or die "$file: $!";
while (my $line = <$fh>){
    print "Better than cat: $line";
}
$fh->close();

use FileHandle;
...
my $handle = FileHandle->new("< $file_to_read");
croak( "Could not open '$file_to_read'" ) unless $handle;
...
my $line1 = <$handle>;
my $line2 = $handle->getline;
my @lines = $handle->getlines;
$handle->close;