使用Perl通过Windows中的批处理文件调用重复数据删除 Windows中的DOS窗口通过批处理文件调用。 批处理文件调用执行操作的Perl脚本。我有批处理文件。 只要数据文件不是太大,我工作的代码脚本就会删除重复数据。 需要解决的问题是数据文件较大(2 GB或更多),在此文件大小时,尝试将完整文件加载到阵列中以便删除重复数据时会发生内存错误。 内存错误发生在以下子程序中: -
@contents_of_the_file = <INFILE>;
(一种完全不同的方法是可以接受的,只要它解决了这个问题,请建议)。 子程序是: -
sub remove_duplicate_data_and_file
{
open(INFILE,"<" . $output_working_directory . $output_working_filename) or dienice ("Can't open $output_working_filename : INFILE :$!");
if ($test ne "YES")
{
flock(INFILE,1);
}
@contents_of_the_file = <INFILE>;
if ($test ne "YES")
{
flock(INFILE,8);
}
close (INFILE);
### TEST print "$#contents_of_the_file\n\n";
@unique_contents_of_the_file= grep(!$unique_contents_of_the_file{$_}++, @contents_of_the_file);
open(OUTFILE,">" . $output_restore_split_filename) or dienice ("Can't open $output_restore_split_filename : OUTFILE :$!");
if ($test ne "YES")
{
flock(OUTFILE,1);
}
for($element_number=0;$element_number<=$#unique_contents_of_the_file;$element_number++)
{
print OUTFILE "$unique_contents_of_the_file[$element_number]\n";
}
if ($test ne "YES")
{
flock(OUTFILE,8);
}
}
答案 0 :(得分:6)
您无需在@contents_of_the_file
中存储原始文件的完整副本,并且 - 如果重复数量相对于文件大小较低 - %unique_contents_of_the_file
中的近两个完整副本和{ {1}}。如@unique_contents_of_the_file
所述,您可以通过对数据进行两次传递来减少存储要求:(1)分析文件,存储有关非重复行的行号的信息; (2)再次处理文件以将非重复写入输出文件。
这是一个例子。我不知道我是否选择了散列函数的最佳模块(Digest::MD5);也许其他人会对此发表评论。另请注意ire_and_curses
的3参数形式,您应该使用它。
open()
答案 1 :(得分:4)
您应该能够使用散列有效地执行此操作。您不需要存储来自行的数据,只需确定哪些是相同的。所以......
在此过程结束时,您将拥有一个标识所有重复行的数据结构。然后,您可以再次通过该文件以删除这些重复项。
答案 2 :(得分:2)
Perl使用大文件做英雄事,但2GB可能是DOS / Windows的限制。
你有多少内存?
如果您的操作系统没有抱怨,最好一次读取一行文件,然后立即写入输出。
我正在考虑使用钻石操作符&lt;&gt;但是我不愿意提出任何代码,因为在我发布代码的情况下,我冒犯了一个Perl大师。
我宁愿不冒险。我希望Perl骑兵很快到来。
与此同时,here's一个链接。
答案 3 :(得分:1)
无论文件有多大,这都是有效的解决方案。但它并不专门使用RAM,因此它比基于RAM的解决方案慢。您还可以指定希望此内容使用的RAM量。
该解决方案使用程序将SQLite视为数据库的临时文件。
#!/usr/bin/perl
use DBI;
use Digest::SHA 'sha1_base64';
use Modern::Perl;
my $input= shift;
my $temp= 'unique.tmp';
my $cache_size_in_mb= 100;
unlink $temp if -f $temp;
my $cx= DBI->connect("dbi:SQLite:dbname=$temp");
$cx->do("PRAGMA cache_size = " . $cache_size_in_mb * 1000);
$cx->do("create table x (id varchar(86) primary key, line int unique)");
my $find= $cx->prepare("select line from x where id = ?");
my $list= $cx->prepare("select line from x order by line");
my $insert= $cx->prepare("insert into x (id, line) values(?, ?)");
open(FILE, $input) or die $!;
my ($line_number, $next_line_number, $line, $sha)= 1;
while($line= <FILE>) {
$line=~ s/\s+$//s;
$sha= sha1_base64($line);
unless($cx->selectrow_array($find, undef, $sha)) {
$insert->execute($sha, $line_number)}
$line_number++;
}
seek FILE, 0, 0;
$list->execute;
$line_number= 1;
$next_line_number= $list->fetchrow_array;
while($line= <FILE>) {
$line=~ s/\s+$//s;
if($next_line_number == $line_number) {
say $line;
$next_line_number= $list->fetchrow_array;
last unless $next_line_number;
}
$line_number++;
}
close FILE;
答案 4 :(得分:0)
在“完全不同的方法”类别中,如果你有Unix命令(例如Cygwin):
cat infile | sort | uniq > outfile
这应该有效 - 根本不需要Perl - 这可能会,也可能不会解决你的记忆问题。但是,您将丢失infile的顺序(因为outfile现在将被排序)。
编辑:可以使用以下算法更好地处理大型文件的替代解决方案:
对于非常大的文件,这比啜食更安全。
第2部分&amp; 3可以更改为随机#而不是散列数mod 10.
这是一个可能有帮助的脚本BigSort(虽然我没有测试过):
# BigSort
#
# sort big file
#
# $1 input file
# $2 output file
#
# equ sort -t";" -k 1,1 $1 > $2
BigSort()
{
if [ -s $1 ]; then
rm $1.split.* > /dev/null 2>&1
split -l 2500 -a 5 $1 $1.split.
rm $1.sort > /dev/null 2>&1
touch $1.sort1
for FILE in `ls $1.split.*`
do
echo "sort $FILE"
sort -t";" -k 1,1 $FILE > $FILE.sort
sort -m -t";" -k 1,1 $1.sort1 $FILE.sort > $1.sort2
mv $1.sort2 $1.sort1
done
mv $1.sort1 $2
rm $1.split.* > /dev/null 2>&1
else
# work for empty file !
cp $1 $2
fi
}
答案 5 :(得分:0)
你可以使用命令行perl的内联替换模式。
perl -i~ -ne 'print unless $seen{$_}++' uberbigfilename