2012-12-13 195 views
4

我正在審查一個測試,我似乎無法得到這個例子來編寫正確的代碼。將兩個文件合併爲第三個使用perl

問題:編寫一個名爲ileaf的perl腳本,它將文件的行與另一個文件的行結合起來,將結果寫入第三個文件。如果這些文件的長度不同,那麼多餘的行會寫在最後。

樣本調用: ileaf文件1文件2 OUTFILE

這是我有:

#!/usr/bin/perl -w 

open(file1, "$ARGV[0]"); 
open(file2, "$ARGV[1]"); 
open(file3, ">$ARGV[2]"); 

while(($line1 = <file1>)||($line2 = <file2>)){ 
    if($line1){ 
      print $line1; 
    } 

    if($line2){ 
      print $line2; 
    } 
} 

這將信息發送到屏幕,這樣我可以立即看到結果。最後的版本應該「打印file3 $ line1;」我得到file1的全部,然後全部file2 w/out和行的交錯。

如果我理解正確,這是一個函數使用「||」在我的while循環中。 while檢查第一個比較結果,如果它真的落入循環。哪個只會檢查file1。一旦file1爲false,那麼while檢查file2並再次進入循環。

我能做些什麼來交錯線?

回答

7

你沒有得到你想要的while(($line1 = <file1>)||($line2 = <file2>)){,因爲只要($line1 = <file1>)爲真,($line2 = <file2>)從來沒有發生過。

嘗試這樣代替:

open my $file1, "<", $ARGV[0] or die; 
open my $file2, "<", $ARGV[1] or die; 
open my $file3, ">", $ARGV[2] or die; 

while (my $f1 = readline ($file1)) { 
    print $file3 $f1; #line from file1 

    if (my $f2 = readline ($file2)) { #if there are any lines left in file2 
    print $file3 $f2; 
    } 
} 

while (my $f2 = readline ($file2)) { #if there are any lines left in file2 
    print $file3 $f2; 
} 

close $file1; 
close $file2; 
close $file3; 
+0

這是工作。我想我試圖將太多的步驟合併到一個循環中。謝謝蒂姆。 – Larry

+4

當然,'readline($ file1)'通常寫成'<$file1>'。如果需要,打印到STDOUT並將輸出重定向到文件也更爲常見(因爲它更加靈活)。 – ikegami

1

只注意到蒂姆A已經發布了一個很好的解決方案。這個解決方案有點小氣,但可能會更清楚地說明發生了什麼。

我使用的方法將兩個文件中的所有行讀取到兩個數組中,然後使用計數器遍歷它們。

#!/usr/bin/perl -w 
use strict; 

open(IN1, "<", $ARGV[0]); 
open(IN2, "<", $ARGV[1]); 

my @file1_lines; 
my @file2_lines; 

while (<IN1>) { 
    push (@file1_lines, $_); 
} 
close IN1; 
while (<IN2>) { 
    push (@file2_lines, $_); 
} 
close IN2; 

my $file1_items = @file1_lines; 
my $file2_items = @file2_lines; 

open(OUT, ">", $ARGV[2]); 
my $i = 0; 
while (($i < $file1_items) || ($i < $file2_items)) { 
    if (defined($file1_lines[$i])) { 
     print OUT $file1_lines[$i]; 
    } 
    if (defined($file2_lines[$i])) { 
     print OUT $file2_lines[$i]; 
    } 
    $i++ 
} 
close OUT; 
2

你會想如果他們教你Perl,他們會使用現代的Perl語法。請不要親自接受。畢竟,這是你如何受教的。但是,您應該瞭解新的Perl編程風格,因爲它有助於消除各種編程錯誤,並使您的代碼更易於理解。

  • 使用編譯指示use strict;use warnings;。警告編譯指示取代了命令行上-w標誌的需要。它實際上更靈活,更好。例如,當我知道他們會成爲問題時,我可以關閉特定的警告。 use strict;編譯指示要求我通過我的我們的聲明我的變量。 (注意:不要在變量中聲明Perl)。 99%的時間,您將使用我的。這些變量被稱爲詞法範圍,但您可以將其視爲真正的局部變量。詞彙範圍變量在範圍之外沒有任何值。例如,如果您在while循環內聲明使用my的變量,那麼一旦循環退出,該變量就會消失。
  • 使用open語句的三個參數語法:在下面的示例中,我使用三個參數語法。這樣,如果一個文件被稱爲>myfile,我將能夠讀取它。
  • **使用本地定義的文件句柄。請注意,我使用my $file_1_fh而不是簡單的FILE_1_HANDLE。舊的方式,FILE_1_HANDLE是全局作用域,再加上文件句柄傳遞給函數是非常困難的。使用詞法範圍的文件句柄更好。
  • 使用orand,而不是||&&:他們更容易理解,他們的運算符優先級比較好。他們更可能不會造成問題。
  • 總是檢查您的open聲明是否有效:您需要確保您的open聲明實際上打開了一個文件。或者使用use autodie;編譯如果open語句失效,這會殺了你的程序(這可能是您無論如何要做些什麼

而且,這裏是你的程序:

#! /usr/bin/env perl 
# 

use strict; 
use warnings; 
use autodie; 

open my $file_1, "<", shift; 
open my $file_2, "<", shift; 
open my $output_fh, ">", shift; 

for (;;) { 
    my $line_1 = <$file_1>; 
    my $line_2 = <$file_2>; 
    last if not defined $line_1 and not defined $line_2; 
    no warnings qw(uninitialized); 
    print {$output_fh} $line_1 . $line_2; 
    use warnings; 
} 

在上面的例子中,我從兩個文件讀即使他們是空。如果沒有什麼閱讀,然後$line_1$line_2簡直是不確定的。我做我的閱讀後,我檢查都$line_1$line_2是否是不確定的。如果是這樣,我用last結束我的循環。

因爲我的文件句柄是一個標量變量,所以我喜歡把它放在花括號中,所以人們知道它是一個文件句柄,而不是我想要打印的變量。我不需要它,但它提高了清晰度。

請注意no warnings qw(uninitialized);。這將關閉我將得到的未初始化警告。我知道$line_1$line_3可能未初始化,所以我不想要警告。我把它重新放在我的印刷聲明下面,因爲這是一個有價值的警告。

這裏的另一種方式做到這一點for循環:

while (1) { 
    my $line_1 = <$file_1>; 
    my $line_2 = <$file_2>; 
    last if not defined $line_1 and not defined $line_2; 
    print {$output_fh} $line_1 if defined $line_1; 
    print {$output_fh} $line_2 if defined $line_2; 
} 

無限循環是一個while循環,而不是爲循環。有些人不喜歡for循環的C風格,並且已經禁止了它的編碼實踐。因此,如果您有無限循環,則使用while (1) {。對我來說,也許是因爲我來自C背景,for (;;) {意味着無限循環while (1) {需要幾個額外的毫秒來消化。

此外,我檢查$line_1$line_2是否在我打印出來之前定義。我想這比使用no warningwarning好,但我需要兩個單獨的打印語句,而不是將它們組合成一個。

+0

在這個特例中,我會爭辯說'''而不是'或'是正確的選擇。我的一般經驗法則是邏輯運算的'||'('if($ a || $ b)')和'or'用於流量控制('open或die'),因爲這種期望趨於匹配運算符優先級規則。 –

+0

我想這是一種風格偏好。主要是用'或'和'和'優先順序較低,並且在不用括號括起時通常效果更好。作爲一個老C程序員,我也認爲''''和'&&'在邏輯上看起來更好,但是如果你想淡化C風格for循環,我想其他C的東西也應該被棄用。 。 –

2

下面是一個使用List::MoreUtilszip交錯陣列和File::Slurp讀取和寫入文件的另一種選擇:

use strict; 
use warnings; 
use List::MoreUtils qw/zip/; 
use File::Slurp qw/read_file write_file/; 

chomp(my @file1 = read_file shift); 
chomp(my @file2 = read_file shift); 

write_file shift, join "\n", grep defined $_, zip @file1, @file2; 
相關問題