2012-12-13 175 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; 
相关问题