我正在检查测试,我似乎无法通过这个示例来编写正确的代码。
问题:编写一个名为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并再次进入循环。
我可以做些什么来交错线?
答案 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编程风格,因为它有助于消除各种编程错误,并使您的代码更易于理解。
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是全局范围的,而且将文件句柄传递给函数非常困难。使用词法范围的文件句柄可以更好地工作。or
和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_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 warning
和warning
更好,但我需要两个单独的打印语句,而不是将它们合并为一个。
答案 2 :(得分: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;
答案 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;