4

我正在审查一个测试,我似乎无法让这个示例正确编码。

问题:编写一个名为 ileaf 的 perl 脚本,它将一个文件的行与另一个文件的行交织,将结果写入第三个文件。如果文件的长度不同,那么多余的行会写在最后。

示例调用:ileaf file1 file2 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,没有和交错的行。

如果我理解正确,这是使用“||”的功能 在我的 while 循环中。while 检查第一次比较,如果为真,则进入循环。这只会检查file1。一旦 file1 为假,while 就会检查 file2 并再次进入循环。

我能做些什么来交错行?

4

4 回答 4

8

你没有得到你想要的,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;
于 2012-12-13T20:09:09.810 回答
2

你会认为如果他们教你 Perl,他们会使用现代 Perl 语法。请不要把这个当成个人。毕竟,这就是你被教导的方式。但是,您应该了解新的 Perl 编程风格,因为它有助于消除各种编程错误,并使您的代码更易于理解。

  • 使用编译指示use strict;use warnings;. 警告编译指示取代了-w命令行上对标志的需要。它实际上更灵活,更好。例如,当我知道它们会成为问题时,我可以关闭特定的警告。use strict;pragma 要求我使用myour声明我的变量。(注意:不要声明 Perl 内置变量)。99% 的时间,你会使用我的. 这些变量称为词法作用域,但您可以将它们视为真正的局部变量。词法范围的变量在其范围之外没有任何值。例如,如果您my在 while 循环中使用声明变量,则该变量将在循环退出后消失。
  • open语句使用三参数语法:在下面的示例中,我使用三参数语法。这样,如果一个文件被调用>myfile,我就可以从中读取。
  • **使用本地定义的文件句柄。请注意,我使用my $file_1_fh而不是简单的 FILE_1_HANDLE。旧方式 FILE_1_HANDLE 是全局范围的,而且很难将文件句柄传递给函数。使用词法范围的文件句柄效果更好。
  • 使用orandand代替||and&&:它们更容易理解,并且它们的运算符优先级更好。他们更有可能不会引起问题。
  • 始终检查您的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_1or$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 风格的for循环,并在他们的编码实践中禁止了它。因此,如果你有一个无限循环,你使用while ( 1 ) {. 对我来说,可能是因为我来自 C 背景,for (;;) {这意味着无限循环,并且while ( 1 ) {需要额外的几毫秒来消化。

另外,我在打印它们之前检查是否定义$line_1或定义。$line_2我想这比使用no warningand更好warning,但我需要两个单独的打印语句,而不是将它们组合成一个。

于 2012-12-13T21:52:32.430 回答
2

这是另一个使用List::MoreUtils交错zip数组和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;
于 2012-12-13T23:32:24.173 回答
1

刚刚注意到 Tim 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;
于 2012-12-13T20:43:56.457 回答