我正在尝试生成一个名为@names的数组,其中包含allnames.txt中存在但不存在于somenames.txt中的人的名字。我的代码如下:
if(open(SKIPLIST, "somenames.txt")) {
@some = <SKIPLIST>;
}
close(SKIPLIST);
if(open(TESTLIST, "allnames.txt")) {
@all = <TESTLIST>;
}
close(TESTLIST);
foreach $name (@all) {
$name =~ s/[\n\r]//mg;
if (grep {$_ eq $name} @some) {
#Do nothing
}
else {
push(@names, $name);
}
}
print "Leftover: @names";
allnames.txt的内容:
adam
jake
john
troy
somenames.txt的内容:
adam
john
实际输出:
Leftover: adam jake troy
预期产出:
Leftover: jake troy
有人可以解释为什么'亚当'仍在被推动?
答案 0 :(得分:2)
"adam"
已包含在结果中,因为您的@some
数组仅包含"adam\n"
。要修复它,只需执行
chomp @some, @all;
或者,如果你想对DOS换行符偏执,
s/[\r\n]+$// for @some, @all;
在你的主循环之前。那你也不需要行
$name =~ s/[\n\r]//mg;
在循环中。
此外,如果您希望代码快速,您应该使用哈希而不是@some
数组,如下所示:
my %some;
if (open SKIPLIST, "somenames.txt") {
while (my $name = <SKIPLIST>) {
chomp $name;
undef $some{$name}; # create the key $name in the hash %some
}
close SKIPLIST;
}
my @names;
if (open TESTLIST, "allnames.txt") {
while (my $name = <TESTLIST>) {
chomp $name;
push @names, $name unless exists $some{$name};
}
close TESTLIST;
}
print "Leftover: @names\n";
答案 1 :(得分:1)
问题是你的一些元素有尾随和/或前导空格(\ n或\ r),有些则没有。解决问题的最佳方法是在阅读文件后立即清理它们:
if(open(SKIPLIST, "somenames.txt")) {
@some = <SKIPLIST>;
foreach (@some) { $_ =~ s/[\n\r]//mg; }
}
close(SKIPLIST);
if(open(TESTLIST, "allnames.txt")) {
@all = <TESTLIST>;
foreach (@all) { $_ =~ s/[\n\r]//mg; }
}
close(TESTLIST);
foreach $name (@all) {
if (grep {$_ eq $name} @some) {
#Do nothing
}
else {
push(@names, $name);
}
}
print "Leftover: @names";
答案 2 :(得分:1)
问题在于,您要从TESTLIST
获取的内容中删除新内容,而不是从SKIPLIST
获取的内容中删除新内容。
我使用哈希而不是grep
进行快速查找,因此我的代码更像是
my %some;
while (<SKIPLIST>) {
s/\s+\z//;
++$some{$_};
}
my @names;
while (<TESTLIST>) {
s/\s+\z//;
push @names, $_ if !$some{$_};
}
或者如果你想要一种函数式编程风格的东西,
use List::MoreUtils qw( apply );
my %some = map { $_ => 1 } apply { s/\s+\z//; } <SKIPLIST>;
my @names = grep !$some{$_}, apply { s/\s+\z//; } <TESTLIST>;
如果您有重复的名称,并且想要获得重复的名称,请将!$some{$_}
更改为!$some{$_}++
(在任一代码段中)。
答案 3 :(得分:0)
无需编写循环来迭代这两组名称。使用map
和散列片可以更清楚地了解正在发生的事情。
use strict;
use warnings;
my $fh;
open $fh, '<', 'somenames.txt' or die $!;
chomp(my @some = <$fh>);
open $fh, '<', 'allnames.txt' or die $!;
chomp(my @all = <$fh>);
my %diff = map(($_ => 1), @all);
delete @diff{@some};
print join(' ', "Leftover:", keys %diff), "\n";