我几天前问了一个关于从PERL文件中删除HTML的问题。我是一个n00b,我已经在网站上搜索了我的问题的答案......但不幸的是我找不到任何东西......这可能是因为我和我没有相关的事情。我在看的时候看到了答案。
所以,情况就是这样。我有一个大约20 GB的文本文件的目录。我想从每个文件中删除HTML并将每个文件输出到唯一的文本文件。我已经编写了下面的程序,这似乎可以解决目录中的前12个文本文件(总共有大约12,000个文本文件)......但是......我遇到了几个问题。第一个问题是,在解析了第12个文本文件之后,我开始收到关于深度递归的警告......然后不久之后程序退出,因为我的内存不足。我想我的编程效率非常低。所以,我想知道你们中是否有人发现我的代码有任何明显的错误,这会让我的内存耗尽。 ......一旦我解决了问题,希望我能够做出贡献。
#!/usr/bin/perl -w
#use strict;
use Benchmark;
#get the HTML-Format package from the package manager.
use HTML::Formatter;
#get the HTML-TREE from the package manager
use HTML::TreeBuilder;
use HTML::FormatText;
$startTime = new Benchmark;
my $direct="C:\\Directory";
my $slash='\\';
opendir(DIR1,"$direct")||die "Can't open directory";
my @New1=readdir(DIR1);
foreach $file(@New1)
{
if ($file=~/^\./){next;}
#Initialize the variable names.
my $HTML=0;
my $tree="Empty";
my $data="";
#Open the file and put the file in variable called $data
{
local $/;
open (SLURP, "$direct$slash"."$file") or die "can't open $file: $!";
#read the contents into data
$data = <SLURP>;
#close the filehandle called SLURP
close SLURP or die "cannot close $file: $!";
if($data=~m/<HTML>/i){$HTML=1;}
if($HTML==1)
{
#the following steps strip out any HTML tags, etc.
$tree=HTML::TreeBuilder->new->parse($data);
$formatter=HTML::FormatText->new(leftmargin=> 0, rightmargin=>60);
$Alldata=$formatter->format($tree);
}
}
#print
my $outfile = "out_".$file;
open (FOUT, "> $direct\\$outfile");
print FOUT "file: $file\nHTML: $HTML\n$Alldata\n","*" x 40, "\n" ;
close(FOUT);
}
$endTime = new Benchmark;
$runTime = timediff($endTime, $startTime);
print ("Processing files took ", timestr($runTime));
答案 0 :(得分:2)
@New1
中的文件列表占用了大量空间。
此外,如果您使用的是较早版本的HTML::TreeBuilder
,那么此类的对象可能需要删除,因为它们过去不受自动Perl垃圾回收的影响。
这是一个避免这两个问题的程序,通过逐步读取目录,并使用HTML::FormatText->format_string
格式化文本,隐式删除它创建的任何HTML::TreeBuilder
个对象。
此外,File::Spec
在构建绝对文件路径方面做得更为繁琐,而且它是一个核心模块,因此不需要在您的系统上安装
use strict;
use warnings;
use File::Spec;
use HTML::FormatText;
my $direct = 'C:\Directory';
opendir my $dh, $direct or die "Can't open directory";
while ( readdir $dh ) {
next if /^\./;
my $file = File::Spec->catfile($direct, $_);
my $outfile = File::Spec->catfile($direct, "out_$_");
next unless -f $file;
my $html = do {
open my $fh, '<', $file or die qq(Unable to open "$file" for reading: $!);
local $/;
<$fh>;
};
next unless $html =~ /<html/i;
my $formatted = HTML::FormatText->format_string(
$html, leftmargin => 0, rightmargin => 60);
open my $fh, '>', $outfile or die qq(Unable to open "$outfile" for writing: $!);
print $fh "File: $file\n\n";
print $fh "$formatted\n";
print $fh "*" x 40, "\n" ;
close $fh or die qq(Unable to close "$outfile" after writing: $!);
}
答案 1 :(得分:1)
您的previous问题的答案出了什么问题?
用于写入的打开文件而不检查返回码。你确定成功了吗?你在哪个目录创建了文件?
更好的方法是:
类似的东西:
while ( my $file = readdir DIR ) {
....process file
open my $newfile, '>', "$direct/out_$outfile" or die "cannot open $outfile: $!\n";
... etc
}
答案 2 :(得分:0)
将$tree = $tree->delete
添加到循环结尾时,问题是否仍然存在?
perl垃圾收集器无法解析循环引用;所以你必须手动销毁树,这样就不会耗尽内存。
(参见http://metacpan.org/pod/HTML::TreeBuilder模块文档中的第一个例子)
您应该将readdir
放在循环中。你编写它的方式,你首先阅读这个巨大的文件列表。当你说
my $file;
while (defined($file = readdir DIR1)) {..}
一次只能读取一个条目。应该节省一些额外的记忆。
您为$tree
提供默认值"Empty"
。这完全没必要。如果要显示变量的未定义方式,请将其设置为undef
,默认情况下为if ($file=~/^\./){next;}
。 Perl保证了这种初始化。
您使用反斜杠作为目录分隔符吗?别担心,只需使用普通斜线。除非你在DOS上,否则你也可以使用正常的斜杠,Windows并不是那么愚蠢。
这一行
next if $file =~ /^\./;
可写得更具可读性
/<HTML>/i
您使用parens作为函数参数列表是不可能的。除非存在歧义,否则可以省略所有内置函数的parens。我更喜欢避开它们,其他人可能会觉得它们更容易阅读。但请坚持一种风格!
您测试是否存在html
。如果我告诉你/<html/i
标签可以有属性怎么办?您应该考虑测试if($data=~m/<HTML>/i){$HTML=1;}
if($HTML==1) {...}
。
你的考试
$HTML = $data =~ /<html/i;
if ($HTML == 1) {...}
可以写成
$HTML = $data =~ /<html/i
if ($HTML) {...}
可以写成
if ($data =~ /<html/i) {...}
可折叠成
$HTML
您实现它的方式,HTML::FormatText
变量从未重置为 false 值。因此,一旦文件包含html,所有后续文件也将被视为html。你可以通过在最内在的合理范围内定义你的变量来抵消这些问题。
充分利用您使用的模块。看看我在my $string = HTML::FormatText->format_file(
'test.html',
leftmargin => 0, rightmargin => 50
);
的示例中找到的内容:
use strict
您可以轻松地调整它以避免手动构建树。为什么你没有尝试过这种方法,就像@pavel在your other post告诉你的那样?本来可以节省你的记忆问题......
你为什么评论$file
?在学习语言时,尽可能多地发出致命警告非常重要。或者在编写固体代码时。那将迫使您明智地声明所有变量,如use warnings
。而不是-w
而不是close
开关,这有点过时了。
但是检查{{1}} ;-)的返回值是一个非常大的“做得好”这是非常不可靠的!