2

小さいファイルと大きいファイルの 2 つのファイルがあります。小さなファイルは、大きなファイルのサブセットです。

例えば:

小さなファイル:

solar:1000
alexey:2000

大きなファイル:

andrey:1001
solar:1000
alexander:1003
alexey:2000

Small.txt にも存在する Big.txt からすべての行を削除したいと考えています。つまり、小さいファイルに共通する大きなファイルの行を削除したいのです。

そこで、以下に示すように Perl スクリプトを作成しました。

#! /usr/bin/perl

use strict;
use warnings;

my ($small, $big, $output) = @ARGV;

open(BIG, "<$big") || die("Couldn't read from the file: $big\n");
my @contents = <BIG>;
close (BIG);

open(SMALL, "<$small") || die ("Couldn't read from the file: $small\n");

while(<SMALL>)
{
    chomp $_;
    @contents = grep !/^\Q$_/, @contents;
}

close(SMALL);

open(OUTPUT, ">>$output") || die ("Couldn't open the file: $output\n");

print OUTPUT @contents;
close(OUTPUT);

ただし、この Perl スクリプトは Big.txt 内の Small.txt と共通の行を削除しません。

このスクリプトでは、最初に大きなファイル ストリームを開き、コンテンツ全体を配列 @contents にコピーします。次に、小さなファイルの各エントリを繰り返し処理し、大きなファイルにそのエントリが存在するかどうかを確認します。Big File から行をフィルター処理し、配列に保存し直します。

このスクリプトが機能しない理由がわかりません。ありがとう

4

3 に答える 3

4

grepはループからの古い値を使用$_して引き継ぐため、スクリプトは機能しません(たとえば、正規表現で使用する変数は、ループ値をブロックに格納するために使用される変数ではありません。同じ名前ですが、スコープが異なります)。grep$_$_while

代わりに名前付き変数を使用してください ($_この種のバグを正確に回避するために、原則として、1 行を超えるコードには絶対に使用しないでください)。

while (my $line=<SMALL>) {
    chomp $line;
    @contents = grep !/^\Q$line/, @contents;
}

ただし、Oleg が指摘したように、より効率的な解決策は、小さなファイルの行をハッシュに読み取ってから、大きなファイルを一度処理し、ハッシュの内容を確認することです (スタイルも少し改善しました - 自由に研究して将来使用してください。レキシカルファイルハンドル変数、open の 3 引数形式、および を介した IO エラー出力の使用$!:

#! /usr/bin/perl

use strict;
use warnings;

my ($small, $big, $output) = @ARGV;

use File::Slurp;
my @small = read_file($small);
my %small = map { ($_ => 1) } @small;

open(my $big, "<", $big) or die "Can not read $big: Error: $!\n";
open(my $output, ">", $output) or die "Can not write to $output: Error: $!\n";

while(my $line=<$big>) {
    chomp $line;
    next if $small{$line}; # Skip common
    print $output "$line\n";
}

close($big);
close($output);
于 2012-06-04T12:23:47.237 に答える
3

いくつかの理由で機能しません。まず、inの行には@contentまだ改行が含まれています。次にgrep$_in!/^\Q$_/が小さなファイルの最後の行ではなく、@contents配列の各要素に設定されているため、効果的に作成されます。リスト内の各要素に対して、この要素を除くすべてが返されます。最後に空のリストを残します。

これは実際には良い方法ではありません。大きなファイルを読み取ってから、それを数回再処理しようとしています。まず、小さなファイルを読み取り、すべての行をハッシュに入れます。次に、ループ内の大きなファイルを読み取りますwhile(<>)。これにより、メモリを無駄にせずに完全に読み取ることができます。各行で、以前に入力されたハッシュを入力するかどうかを確認し、入力する場合は反復にexists進みます。そうでない場合は、行を出力します。next

于 2012-06-04T12:11:50.120 に答える