如果您担心内存占用,您真的想在树枝处理程序中使用刷新/清除。
这样它在文件被解析时被调用。
您对 purge 的调用是在 您的 parsefile 之后进行的,这意味着 - 它必须首先加载并解析整个内容。
您也许可以将其中的一部分构建到您的 trade_handler 中 - 例如你测试一个最大版本,然后在你的迭代循环中比较它。因此,您可以可能在处理程序期间测试该条件:
if ( $max_version{$ref} > $version ) { $trade -> purge; }
但请记住,如果您这样做了,您需要重新考虑您的解析后 foreach 循环,因为您会在执行过程中丢弃。
我不完全确定你的grep 是干什么用的。您也可以在您的 trade_handler 中实现该逻辑,但我不能肯定地说。 (例如,阴性测试,如果不需要此元素,则清除)。
我认为 - 从根本上讲 - 您应该能够为您的“for”循环使用处理程序,并在执行过程中进行处理和清除。我不能确定——你可能需要两遍方法,因为需要提前查找版本号。
编辑:这并不完全符合您的要求,但希望能说明我的建议:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
open( my $out, '>:utf8', 'out.xml' )
or die "cannot create output file out.xml: $!";
my $twig = new XML::Twig(
twig_roots => {
'/STOCKEXT/STOCK/STOCK' => sub { $_->delete() },
'/STOCKEXT/STOCK[@origin != "ASIA"]' => sub { $_->delete; },
'/STOCKEXT/STOCK' => \&trade_handler
},
att_accessors => [qw/ ref version /],
pretty_print => 'indented',
);
my %max_version;
my %best_version_of;
local $/;
$twig->parse(<DATA>);
foreach my $ref ( keys %best_version_of ) {
$best_version_of{$ref} -> print;
}
#$twig->parsefile('1513.xml');
sub trade_handler {
my ( $twig, $trade ) = @_;
my ( $ref, $version ) = ( $trade->ref, $trade->version );
if ( not exists $max_version{$ref}
or $max_version{$ref} < $version )
{
###something here that replicates your grep test, as I'm not sure I've got it right.
$max_version{$ref} = $version;
$best_version_of{$ref} = $trade;
}
$trade -> purge;
}
__DATA__
<STOCKEXT>
<STOCK origin = "ASIA" ref="12" version="1" >
<event eventtype="ghi">
<subevent code = "abc" narattive = "def" />
</event>
</STOCK>
<STOCK origin = "ASIA" ref="12" version="2" >
<event eventtype="ghi">
<subevent code = "abc" narattive = "def" />
</event>
</STOCK>
<STOCK origin = "ASI" ref="13" version="1" >
<event eventtype="ghi">
<subevent code = "abc" narattive = "def" />
</event>
</STOCK>
</STOCKEXT>
如前所述,这并不完全符合您的要求 - 只要您能够通过清除大量 XML 来丢弃它,它就会“节省内存”...但是因为直到最后你才知道哪个版本是最高的,内存占用是不可避免的,因为在你到达那里之前,你永远不会完全知道可以安全地丢弃什么。
因此,也许您需要一种两遍方法,首先解析以提取“最高版本号” - 就像您正在做的那样,但在进行时清除...然后在您了解它们后重新开始,因为然后你知道你可以在你去的时候清除或冲洗什么。
您遇到此问题的原因是,在您到达文件末尾之前,您无法知道自己是否获得了最新版本。
所以你可能需要这样做吗?
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
open( my $out, '>:utf8', 'out.xml' )
or die "cannot create output file out.xml: $!";
my $first_pass = new XML::Twig(
twig_roots => {
'/STOCKEXT/STOCK/STOCK' => sub { $_->delete() },
'/STOCKEXT/STOCK[@origin != "ASIA"]' => sub { $_->delete; },
'/STOCKEXT/STOCK' => \&extract_highest_version,
},
att_accessors => [qw/ ref version /],
pretty_print => 'indented',
);
my $main_parse = new XML::Twig(
twig_roots => {
'/STOCKEXT/STOCK/STOCK' => sub { $_->delete() },
'/STOCKEXT/STOCK[@origin != "ASIA"]' => sub { $_->delete; },
'/STOCKEXT/STOCK' => \&trade_handler
},
att_accessors => [qw/ ref version /],
pretty_print => 'indented',
);
my %max_version_of;
$first_pass->parsefile('1513.xml');
$main_parse->parsefile('1513.xml');
sub extract_highest_version {
my ( $twig, $trade ) = @_;
my ( $ref, $version ) = ( $trade->ref, $trade->version );
if ( not exists $max_version_of{$ref}
or $max_version_of{$ref} < $version )
{
$max_version_of{$ref} = $version;
}
$trade->purge;
}
sub trade_handler {
my ( $twig, $trade ) = @_;
my ( $ref, $version ) = ( $trade->ref, $trade->version );
if ( $version >= $max_version_of{$ref}
and ( $trade->first_child('event')->att('eventtype') eq 'ghi' )
and ( $trade->first_child('event')->first_child('subevent')->att('code') eq 'abc' )
and ( $trade->first_child('event')->first_child('subevent') ->att('narattive') eq 'def' )
)
{
$trade->flush;
}
else {
$trade->purge;
}
}
可能会更整洁一些,但重点是 - 你跑过一次 - 和purge ,所以在给定的时间你只有一个<STOCK> 在内存中。那么你就有了“ref”和“highest version”之间的关系。
然后你再解析一次,因为你知道最高版本是什么,你可以随时清除/刷新 - 你不必阅读未来。
现在正如 cmets 中所述 - 您的第一种方法将整个文件读入内存,因为它需要知道“最高版本”。虽然它是单次通过,但速度更快。
另一种方法是两次通过 - 两次读取文件。速度较慢,但根本不会保留太多内存。
一个中途的房子可能是:
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
open( my $out, '>:utf8', 'out.xml' )
or die "cannot create output file out.xml: $!";
my $twig = new XML::Twig(
twig_roots => {
'/STOCKEXT/STOCK/STOCK' => sub { $_->delete() },
'/STOCKEXT/STOCK[@origin != "ASIA"]' => sub { $_->delete; },
'/STOCKEXT/STOCK' => \&trade_handler
},
att_accessors => [qw/ ref version /],
pretty_print => 'indented',
);
my %max_version_of;
my %best_version_of;
$twig->parsefile('1513.xml');
print {$out} "<STOCKEXT>\n";
foreach my $ref ( keys %best_version_of ) {
foreach my $trade ( @{ $best_version_of{$ref} } ) {
$trade->print($out);
}
}
print {$out} "</STOCKEXT>\n";
sub trade_handler {
my ( $twig, $trade ) = @_;
my ( $ref, $version ) = ( $trade->ref, $trade->version );
if (( not defined $max_version_of{$ref}
or $version >= $max_version_of{$ref}
)
and ( $trade->first_child('event')->att('eventtype') eq 'ghi' )
and
( $trade->first_child('event')->first_child('subevent')->att('code')
eq 'abc' )
and ( $trade->first_child('event')->first_child('subevent')
->att('narattive') eq 'def' )
)
{
if ( not defined $max_version_of{$ref}
or $version >= $max_version_of{$ref} )
{
#this version is higher, so anything lower is redundant - remove it
@{ $best_version_of{$ref} } = ();
}
push( @{ $best_version_of{$ref} }, $trade );
$max_version_of{$ref} = $version;
}
$trade->purge;
#can omit this, it'll just print how 'big' the hash is getting.
print "Hash size: ". %best_version_of."\n";
}
它所做的仍然是清除,但会慢慢填满%best_version_of。虽然它可能仍然有很大的内存占用 - 它很大程度上取决于“保留”与“丢弃”的比率。
恐怕没有最佳解决方案 - 要确定哪个版本是“最新的”,您必须运行两次文件,要么以消耗内存为代价,要么以磁盘 IO。
(我想我必须提出一个警告——这不会产生“有效”的 XML,因为根节点 <STOCKEXT> 将被丢弃。将其放在 $out 和 </STOCKEXT> 的开头end 会解决这个问题)。