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);

私が間違っていなければ、2番目のループが開始されないというエラーはありません。

厳密に:

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 が言うように後者は宣言されていません。

「厳密な」実装のもう1つの問題は、ループ内でファイルを読み取っていることです。これは、ネストされたループの場合、外側のループの最初の反復でファイル 2 を読み取り、後続のすべての反復で内側のループが行を読み取ることができないことを意味します。

stevenl によって指摘された foreach/while の問題を見逃していました。

unstrict スクリプトの何が問題なのかわかりません。

しかし、2 つのファイルを処理するためにネストされたループを使用することはまったくありません。ループのネストを解除すると、大まかに次のようになります。

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

非厳密バージョンの問題がどこにあるのか正確にはわかりません。あなたが遭遇している問題は何ですか?

厳密なバージョンの問題は、特に次の 2 行にあります。

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 をリセットしません。したがって、内側のループが 2 回目に実行されるときは、配列の終わりを過ぎた要素にアクセスしようとしています。この間違いは、厳密バージョンと非厳密バージョンの両方に存在します。

$i と $j をまったく使用しないでください。foreach のポイントは、各要素を自動的に取得することです。内側のループで foreach を正しく使用する例を次に示します。

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

    #...now do stuff as before
}

これは、すべての配列を処理するまで、@infile one の各要素を変数 $line に連続して入れます。

于 2012-08-13T11:06:27.603 に答える