2011-02-02 63 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';