列中重复值处理问题:Perl脚本重复输出结果求助
解决Perl脚本重复输出的问题
我来帮你排查这个脚本的问题,顺便给你修正方案~
你的问题背景
原始数据
你有一个包含交易记录的文件,格式如下:
02-May-2018,AAPL,Sell,0.25,1000 02-May-2018,C,Sell,0.25,2000 02-May-2018,JPM,Sell,0.25,3000 02-May-2018,WFC,Sell,0.25,5000 02-May-2018,AAPL,Sell,0.25,7000 02-May-2018,GOOG,Sell,0.25,8000 02-May-2018,GOOG,Sell,0.25,9000 02-May-2018,C,Sell,0.25,2000 02-May-2018,AAPL,Sell,0.25,3000
需求
当第二列的股票代码出现次数超过2次时,输出该代码对应的所有原始行。比如AAPL出现了3次,需要输出它的全部3条记录。
有问题的脚本
你写的脚本会重复输出结果,不符合预期:
open (FILE, "<$TMPFILE") or die "Could not open $TMPFILE"; open (OUT, ">$TMPFILE1") or die "Could not open $TMPFILE1"; %count = (); @symbol = (); while ($line = <FILE>) { chomp $line; (@data) = split(/,/,$line); $count{$data[1]}++; @keys = sort {$count{$a} cmp $count{$b}} keys %count; for my $key (@keys) { if ( $count{$key} > 2 ) { print "$line\n"; } } }
问题根源
你的脚本逻辑顺序错了:
- 你每读一行就立刻更新计数,然后遍历所有已统计的股票代码——只要有任何一个代码计数超过2,就打印当前行。这会导致同一行被多次输出(比如当多个代码计数都达标时,当前行被打印多遍)。
- 而且你是边读行边判断,此时计数还没统计完。比如读到第三行AAPL时,计数刚到3,这时候会打印第三行,但前面的两行AAPL根本没被输出,因为之前它们的计数还没到3。
简单说,你的逻辑是读一行就检查所有计数,符合条件就打当前行,这既会重复打印,也会漏掉前面符合条件的行。
修正后的脚本
正确的思路应该是先统计所有股票的出现次数,再重新读取文件输出符合条件的行,这样能保证只输出正确的行,且不重复。
方案一:两次读取文件(适合大文件,内存占用低)
use strict; use warnings; # 这两行是Perl编程的好习惯,能帮你提前发现潜在错误 my $TMPFILE = 'your_input_file.txt'; # 替换成你的实际输入文件名 my $TMPFILE1 = 'output.txt'; # 替换成你的实际输出文件名 # 第一步:遍历文件,统计每个股票代码的出现次数 my %count; open(my $in_fh, '<', $TMPFILE) or die "Could not open $TMPFILE: $!"; while (my $line = <$in_fh>) { chomp $line; my @data = split(/,/, $line); $count{$data[1]}++; } close $in_fh; # 第二步:再次遍历文件,输出符合条件的行 open(my $out_fh, '>', $TMPFILE1) or die "Could not open $TMPFILE1: $!"; open($in_fh, '<', $TMPFILE) or die "Could not open $TMPFILE again: $!"; while (my $line = <$in_fh>) { chomp $line; my @data = split(/,/, $line); if ($count{$data[1]} > 2) { print $out_fh "$line\n"; # 输出到指定文件,而不是控制台 } } close $in_fh; close $out_fh;
方案二:一次读取所有行到内存(适合小文件,代码更简洁)
如果你的文件不大,可以先把所有行存到数组里,再统计计数,最后遍历数组输出:
use strict; use warnings; my $TMPFILE = 'your_input_file.txt'; my $TMPFILE1 = 'output.txt'; my @all_lines; my %count; # 读取所有行到数组,同时统计计数 open(my $in_fh, '<', $TMPFILE) or die "Could not open $TMPFILE: $!"; while (my $line = <$in_fh>) { chomp $line; push @all_lines, $line; my @data = split(/,/, $line); $count{$data[1]}++; } close $in_fh; # 输出符合条件的行 open(my $out_fh, '>', $TMPFILE1) or die "Could not open $TMPFILE1: $!"; foreach my $line (@all_lines) { my @data = split(/,/, $line); if ($count{$data[1]} > 2) { print $out_fh "$line\n"; } } close $out_fh;
额外优化说明
- 用
use strict;和use warnings;能帮你捕获变量未声明、类型错误等问题,避免很多隐性bug。 - 使用词法文件句柄(
$in_fh、$out_fh)代替旧式的FILE、OUT,更安全且符合现代Perl规范。 - 在
die语句中加上$!,能输出具体的系统错误原因,方便调试。
内容的提问来源于stack exchange,提问作者Efficasy
相关产品推荐
相关产品推荐

