2011-02-02 66 views
3

就地编辑的大多数示例是遍历一个或多个文件的单行程序,每次读取和打印一行。读取整个文件,然后在就地编辑时打印?

我找不到任何将整个文件读入数组,根据需要修改数组,然后使用^ I开关执行就地编辑时打印数组的任何示例。当我尝试从钻石操作员读取整个文件时,编辑内容并打印整个内容,我发现打印输出到STDOUT而不是ARGVOUT,并且ARGVOUT已关闭。我可以打开相同的文件输出,然后打印到它,但我不知道我明白为什么这是必要的。这里有一个例子:

#!/usr/bin/perl 
use strict; 
use warnings; 
use 5.010; 

my $filename = 'test.txt'; 

push @ARGV, $filename; 

$^I = ".bk"; 

my @file = <>; #Read all records into array 
chomp @file; 
push @file, qw(add a few more lines); 

print join "\n", @file; #This prints to STDOUT, and ARGVOUT is closed. Why? 

运行上面,使预期中的test.txt文件的备份,但保留编辑的test.txt空,打印编辑的内容输出到标准输出代替。

+2

为什么你找不到任何的例子,是因为在Perl中读取整个文件通常被认为是不好的做法,只能进行逐行处理。 :)有很多更好的方法来处理阅读。请参阅下面的答案,了解几个具体原因。 – 2011-02-02 21:39:47

+2

对不起@Robert P,但是有很多线处理任务,最容易首先加载所有线。如果你想删除文件中间的行,该怎么办?在包含其他图案的线条之前,删除包含700至750行*之间图案的线条?在排序后处理输入,然后在打印前删除顶部和/或底部的一些行? – mob 2011-02-02 23:04:48

+0

伟大的答案。 @ mob和@ ephemient都是我想要的东西,所以真的是要抛弃,接受。 – d5e5 2011-02-03 19:15:27

回答

6

请参阅​​。

-i开关被调用时,perl使用ARGVOUT作为默认文件句柄而不是STDOUT来启动程序。如果有多个输入文件,则每当<><ARGV>readline(ARGV)操作完成其中一个输入文件时,它将关闭ARGVOUT并重新打开它以写入下一个输出文件名。

一旦所有来自<>的输入耗尽(当没有更多的文件要处理时),perl关闭ARGVOUT并且恢复STDOUT作为默认文件句柄。或者正如perlrun说,一旦你说my @file = <>和消耗所有的输入,Perl的关闭文件句柄到备份文件,并启动再次定向输出到STDOUT

#!/usr/bin/perl -pi.orig 
s/foo/bar/; 

相当于

#!/usr/bin/perl 
$extension = '.orig'; 
LINE: while (<>) { 
    if ($ARGV ne $oldargv) { 
     if ($extension !~ /\*/) { 
      $backup = $ARGV . $extension; 
     } 
     else { 
      ($backup = $extension) =~ s/\*/$ARGV/g; 
     } 
     rename($ARGV, $backup); 
     open(ARGVOUT, ">$ARGV"); 
     select(ARGVOUT); 
     $oldargv = $ARGV; 
    } 
    s/foo/bar/; 
} 
continue { 
    print; # this prints to original filename 
} 
select(STDOUT); 


的解决方法,我认为,是调用<>在标量上下文并在每行之后检查eof(ARGV)。当eof(ARGV)=1您已经阅读该文件中的最后一行,你会得到一个机会,你再打电话<>之前打印:

my @file =(); 
while (<>) { 
    push @file, $_; 
    if (eof(ARGV)) { 
     # done reading current file 
     @processed_file = &do_something_with(@file); 
     # last chance to print before ARGVOUT gets reset 
     print @processed_file; 
     @file =(); 
    } 
} 
2

Tie::File也可用于编辑就地文件。但是,它不会保留原始文件的备份副本。

use warnings; 
use strict; 
use Tie::File; 

my $filename = 'test.txt'; 
tie my @lines, 'Tie::File', $filename or die $!; 
push @lines, qw(add a few more lines); 
untie @lines; 
3
my @file = <>; #Read all records into array 

是坏的。现在你完成了所有的记录,*ARGV已关闭,$^I替换没有任何工作要做。

my @file; 
while (<>) { 
    push @file, $_; 
} 
continue { 
    if (eof ARGV) { 
     chomp @file; 
     push @file, qw(add a few more lines); 
     print join "\n", @file; 
     @file =(); 
    } 
} 

这是一次一行读取文件,并在每个文件结束时(关闭之前)执行操作。

undef $/; 
while (<>) { 
    my @file = split /\n/, $_, -1; 
    push @file, qw(add a few more lines); 
    print join "\n", @file; 
} 

这将一次读取整个文件作为单个记录。

1

Perl的就地编辑是比任何的答案更简单:

sub edit_in_place 
{ 
    my $file  = shift; 
    my $code  = shift; 
    { 
     local @ARGV = ($file); 
     local $^I = ''; 
     while (<>) { 
      &$code; 
     } 
    } 
} 

edit_in_place $file, sub { 
    s/search/replace/; 
    print; 
}; 

,如果你想创建,然后备份更改local $^I = '';local $^I = '.bak';