4

テストのレビューを行っていますが、この例を正しくコーディングできないようです。

問題: ileaf と呼ばれる perl スクリプトを作成します。このスクリプトは、ファイルの行を別のファイルの行とリンターリーブし、結果を 3 番目のファイルに書き込みます。ファイルの長さが異なる場合、余分な行は最後に書き込まれます。

呼び出しの例: 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;
    }
}

これにより、情報が画面に送信されるため、すぐに結果を確認できます。最終バージョンは「print file3 $line1;」です。私はfile1のすべてを取得してから、file2のすべてを行のインターリーブなしで取得しています。

私の理解が正しければ、これは「||」を使用した関数です。私のwhileループで。while は最初の比較をチェックし、それが true の場合はループに落ちます。file1のみをチェックします。file1 が false になると、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;プラグマでは、myまたはourのいずれかを使用して変数を宣言する必要があります。(注:Perl組み込み変数を宣言しないでください)。99%の時間、あなたは私のを使用します。これらの変数は字句スコープと呼ばれますが、真のローカル変数と考えることができます。字句スコープの変数には、スコープ外の値はありません。たとえばmy、whileループ内を使用して変数を宣言した場合、その変数はループが終了すると消えます。
  • openステートメントには3つのパラメーター構文を使用します。以下の例では、3つのパラメーター構文を使用します。このように、ファイルが呼び出された場合>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_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;
}

無限ループは、forループではなくwhileループです。一部の人々はCスタイルのforループが好きではなく、コーディング慣行からそれを禁止しています。したがって、無限ループがある場合は、を使用しますwhile ( 1 ) {。私にとって、おそらく私がCのバックグラウンドから来たため、無限ループfor (;;) {を意味し、消化するのに数ミリ秒余分にかかります。while ( 1 ) {

また、印刷する前に$line_1、または$line_2が定義されているかどうかを確認します。no warningandを使用するよりはましだと思いますwarningが、1つにまとめるのではなく、2つの別々のprintステートメントが必要です。

于 2012-12-13T21:52:32.430 に答える
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;
于 2012-12-13T23:32:24.173 に答える
1

Tim A がすでに投稿された素晴らしいソリューションを持っていることに気付きました。この解決策は少し冗長ですが、何が起こっているのかをもう少し正確に示している可能性があります。

私が行った方法では、両方のファイルからすべての行を 2 つの配列に読み取り、カウンターを使用してそれらをループ処理します。

#!/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 に答える