0

它应该是一个简单的嵌套 foreach 循环,但它不起作用,并且真的开始让我烦恼,我无法弄清楚这一点!仍然是 perl 初学者,但我想我现在明白了。有人可以向我解释我要去哪里错了吗?这个想法很简单:2 个文件,1 个小文件,1 个大文件,其中包含我想要的小文件中的信息。两者都有唯一的ID。比较并匹配 id 并输出一个新的小文件,其中包含小文件中添加的信息。

我有 2 段代码:1 段没有严格,1 段有且两者都不起作用。我知道使用严格,但我仍然很好奇为什么没有严格的人也不起作用。

无限制:

if ($#ARGV != 2){
print "input_file1 input_file2 output_file\n";
exit;
} 

$inputfile1=$ARGV[0];  
$inputfile2=$ARGV[1]; 
$outputfile1=$ARGV[2]; 

open(INFILE1,$inputfile1) || die "No inputfile :$!\n";
open(INFILE2,$inputfile2) || die "No inputfile :$!\n";
open(OUTFILE_1,">$outputfile1") || die "No outputfile :$!\n";

$i = 0;
$j = 0;

@infile1=<INFILE1>;
@infile2=<INFILE2>;

foreach ( @infile1 ){
    @elements = split(";",$infile1[$i]);

    $id1 = $elements[3];
    print "1. $id1\n";

    $lat = $elements[5];
    $lon = $elements[6];

    $lat =~ s/,/./;
    $lon =~ s/,/./;

    print "2. $lat\n";
    print "3. $lon\n";

    foreach ( @infile2 ){
        @loopelements = split(";",$infile2[$j]);

        $id2 = $loopelements[4];

        print "4. $id2\n";

        if ($id1 == $id2){
        print OUTFILE_1 "$loopelements[0];$loopelements[1];$loopelements[2];$loopelements[3];$loopelements[4];$lat,$lon\n";
        };

        $j = $j+1;
        };

  @elements = join(";",@elements);  # add ';' to all elements
  #print "$i\r";
  $i = $i+1;
  }
close(INFILE1);
close(INFILE2);
close(OUTFILE_1);

如果我没记错的话,第二个循环将不会启动。

有严格要求:

use strict;
use warnings;

my $inputfile1 = shift || die "Give input!\n";
my $inputfile2 = shift || die "Give more input!\n";
my $outputfile = shift || die "Give output!\n";

open my $INFILE1, '<', $inputfile1  or die "In use/Not found :$!\n";
open my $INFILE2, '<', $inputfile2  or die "In use/Not found :$!\n";
open my $OUTFILE, '>', $outputfile  or die "In use/Not found :$!\n";

my $i = 0;
my $j = 0;

foreach ( my $infile1 = <$INFILE1> ){
    my @elements = split(";",$infile1[$i]);

    my $id1 = $elements[3];
    print "1: $id1\n";

    my $lat = $elements[5];
    my $lon = $elements[6];

    $lat =~ s/,/./;
    $lon =~ s/,/./;

    print "2: $lat\n";
    print "3: $lon\n";

    foreach ( my $infile2 = <$INFILE2> ){
        my @loopelements = split(";",$infile2[$j]);

        my $id2 = $loopelements[4];

        print "4: $id2\n";

        if ($id1 == $id2){
        print $OUTFILE "$loopelements[0];$loopelements[1];$loopelements[2];$loopelements[3];$loopelements[4];$lat,$lon\n";
        };

    $j = $j+1;
    };

  #@elements = join(";",@elements); # add ';' to all elements
  #print "$i\r";
  $i = $i+1;
  }
close($INFILE1);
close($INFILE2);
close($OUTFILE);

严格的错误:

Global symbol "@infile1" requires explicit package name at Z:\Data-Content\Data\test\jan\bestemming_zonder_acco\add_latlon_dest_test.pl line 16.
Global symbol "@infile2" requires explicit package name at Z:\Data-Content\Data\test\jan\bestemming_zonder_acco\add_latlon_dest_test.pl line 31.
4

3 回答 3

2

由于混淆符号($ 和 @ 字符)指示变量是标量还是数组,您的“严格”实现会给您带来错误。在循环语句中,您将文件的每一行读入名为 $infile1 的标量中,但在下一行中,您尝试访问数组 @infile1 的元素。这些与变量无关,正如 perl 告诉你的那样,后者没有被声明。

您“严格”实现的另一个问题是您正在循环内读取文件。这意味着对于嵌套循环,您将在外循环的第一次迭代中读取文件 2,而对于所有后续迭代,内循环将无法读取任何行。

我错过了 stevenl 指出的 foreach/while 问题,即使修复了严格问题,也会让你只使用一次迭代就可以使用 foreach 循环。

我不确定你对 unstrict 脚本有什么问题。

但是我根本不会使用嵌套循环来处理两个文件。我会取消嵌套循环,所以它大致看起来像这样:

my %cord;
while ( my $line = <$INFILE1> ) {
    my @elements = split /;/, $line;

    $cord{ $elements[3] } = "$elements[5],$elements[6]";
}

while ( my $line = <$INFILE2> ) {
    my @elements = split /;/, $line;

    if ( exists %coord{ $elements[4] } ) {
        print $OUTFILE "....;$cord{ $elements4 }\n";
    }
}
于 2012-08-13T09:50:40.873 回答
1

我看不出非严格版本的问题到底出在哪里。您遇到的问题是什么?

严格版本的问题尤其在以下两行:

foreach ( my $infile1 = <$INFILE1> ){
    my @elements = split(";",$infile1[$i]);

您在第一行中有一个标量$infile1,但您在下一行将其视为一个数组。此外,将 更改foreach为 a while(见下文)。

几点评论。

  • 对于非严格版本,您可以将循环折叠为 C 样式for循环,如下所示:

    for (my $i = 0; $i < @infile1; $i++) {
        ...
    }
    
  • 如果您完全不使用数组索引,则可以更轻松地阅读:

    foreach my $infile1 (@infile1) {
        my @elements = split ';', $infile1;
        ...
    }
    
  • 但是对于较大的文件,在开始时将整个文件放入数组中可能需要一些时间。所以最好在你去的时候遍历文件:

    while (my $infile = <$INFILE1>) {
        ...
    }
    
  • 注意最后一点应该是严格版本的外观。您需要一个while循环而不是一个foreach循环,因为分配<$INFILE1>给一个标量意味着它将只返回下一行,只要文件中有另一行,它的计算结果为 true。(因此,foreach只会让第一行循环。)

于 2012-08-13T10:03:10.080 回答
0

在内部 foreach 循环运行之前,您不会重置 $j 。因此,第二次运行内部循环时,您将尝试访问超出数组末尾的元素。这个错误在严格和非严格版本中都存在。

您根本不应该使用 $i 和 $j ;foreach 的重点是它会自动为您获取每个元素。这是在内部循环中正确使用 foreach 的示例:

foreach my $line ( @infile2 ){
    @loopelements = split(";",$line);

    #...now do stuff as before
}

这会将@infile 的每个元素依次放入变量 $line 中,直到您遍历所有数组。

于 2012-08-13T11:06:27.603 回答