我有两个文件,一个小文件和一个大文件。小文件是大文件的一个子集。
例如:
小文件:
solar:1000
alexey:2000
大文件:
andrey:1001
solar:1000
alexander:1003
alexey:2000
我想删除Big.txt中也存在于Small.txt中的所有行。换句话说,我想删除Big文件中常用于小文件的行。
所以,我写了一个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中过滤掉这一行并将其保存回数组中。
我不确定为什么这个脚本不起作用?感谢
答案 0 :(得分:4)
您的脚本不起作用,因为grep使用$_
并从循环中接管grep
期间$_
的旧值(例如您在正则表达式中使用的变量$_
不是用于在while
块中存储循环值的变量 - 它们的名称相同,但具有不同的范围。)
使用命名变量(作为规则,永远不要将$_
用于任何超过1行的代码,正是为了避免这种类型的错误):
while (my $line=<SMALL>) {
chomp $line;
@contents = grep !/^\Q$line/, @contents;
}
然而,正如Oleg所指出的,一个更有效的解决方案是将小文件的行读入哈希,然后处理大文件ONCE,检查哈希内容(我也改进了样式 - 随意学习和使用未来,使用词法文件句柄变量,通过$!
打开和IO错误打印的3-arg形式:
#! /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);
答案 1 :(得分:3)
由于几个原因,它不起作用。首先,@content
中的行仍然有其换行符。其次,当grep
时,$_
中的!/^\Q$_/
设置为不是小文件的最后一行,而是@contents
数组的每个元素,有效地使它:对于列表中的每个元素返回除此元素之外的所有内容,最后留下空列表。
这不是真正的好方法 - 你正在阅读大文件,然后尝试多次重新处理它。首先,读取一个小文件并将每一行放入哈希。然后在while(<>)
循环中读取大文件,这样你就不会浪费你的记忆完全阅读它。在每一行上,检查先前填充的哈希值中的密钥exists
是否存在 - 转到next
迭代,否则打印该行。
答案 2 :(得分:1)
以下是解决问题的小型有效解决方案:
#!/usr/bin/perl
use strict;
use warnings;
my ($small, $big, $output) = @ARGV;
my %diffx;
open my $bfh, "<", $big or die "Couldn't read from the file $big: $!\n";
# load big file's contents
my @big = <$bfh>;
chomp @big;
# build a lookup table, a structured table for big file
@diffx{@big} = ();
close $bfh or die "$!\n";
open my $sfh, "<", $small or die "Couldn't read from the file $small: $!\n";
my @small = <$sfh>;
chomp @small;
# delete the elements that exist in small file from the lookup table
delete @diffx{@small};
close $sfh;
# print join "\n", keys %diffx;
open my $ofh, ">", $output or die "Couldn't open the file $output for writing: $!\n";
# what is left is unique lines from big file
print $ofh join "\n", keys %diffx;
close $ofh;
__END__
P.S。我从Perl Cookbook, 2nd Edition学到了这个技巧和许多其他技巧。感谢