使用perl将2个文件合并到第三个文件中

时间:2012-12-13 19:49:34

标签: perl file-io

我正在检查测试,我似乎无法通过这个示例来编写正确的代码。

问题:编写一个名为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然后所有的文件2 w / out和交错的行。

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

我可以做些什么来交错线?

4 个答案:

答案 0 :(得分: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;

答案 1 :(得分:2)

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

  • 使用pragma use strict;use warnings; 。警告pragma替换了命令行上-w标志的需要。它实际上更灵活,更好。例如,当我知道它们会成为一个问题时,我可以关闭特定的警告。 use strict;编译指示要求我使用 my 我们的声明我的变量。 (注意:不要声明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 循环。有些人不喜欢for循环的C风格,并已将其禁止编码。因此,如果您有无限循环,则使用while ( 1 ) {。对我来说,也许是因为我来自C背景,for (;;) {意味着无限循环,而while ( 1 ) {需要几毫秒来消化。

另外,在打印出来之前,我会检查$line_1$line_2是否已定义。我想这比使用no warningwarning更好,但我需要两个单独的打印语句,而不是将它们合并为一个。

答案 2 :(得分: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;

答案 3 :(得分: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;