perlxml::SAX部分解析
是否可以使用perlxml::SAX部分解析,xml,windows,perl,xml-parsing,sax,Xml,Windows,Perl,Xml Parsing,Sax,是否可以使用XML::SAX::Expat的处理程序类暂停并恢复解析 文件非常大,我们正在将节点读入内存以呈现表。我们希望一次只渲染一个部分,否则会耗尽内存。所以我们需要停止解析文件,在程序的其他部分做一些事情,然后继续下一页 我可以想出一些方法来实现这一点(见下文),但它们都像黑客。有什么我可以用的土生土长的吗? 可能的选择: 跟踪$current_节点计数器并将其传递给处理程序构造函数,每个 解析文件时,在存储数据之前跳过这么多节点 与上面类似,但在每次新调用时使用tell/seek跳过
XML::SAX::Expat
的处理程序类暂停并恢复解析
文件非常大,我们正在将节点读入内存以呈现表。我们希望一次只渲染一个部分,否则会耗尽内存。所以我们需要停止解析文件,在程序的其他部分做一些事情,然后继续下一页
我可以想出一些方法来实现这一点(见下文),但它们都像黑客。有什么我可以用的土生土长的吗?
可能的选择:
- 跟踪$current_节点计数器并将其传递给处理程序构造函数,每个 解析文件时,在存储数据之前跳过这么多节点
- 与上面类似,但在每次新调用时使用tell/seek跳过
- 预处理将文件拆分为多个大小正确的文件
编辑以解释有关文件结构的更多信息以及替代方案不起作用的原因。 除了一些其他数据外,结构的主要部分如下所示
<DETAILS>
<DETAIL>
<ITEM1>...</ITEM1>
<ITEM2>...</ITEM2>
...
</DETAIL>
<DETAIL>
<ITEM1>...</ITEM1>
<ITEM2>...</ITEM2>
...
</DETAIL>
...
</DETAILS>
还有另一个编辑。。。似乎在调整了搜索之后,我一直都无意中发现了我想要的东西
XML::SAX::Expat::Incremental
有一个parse\u more
例程,它完全满足我的需要。我需要等几天才能在完整的数据集上进行测试,但下面的一个简短测试是有效的
表
类可以执行以下操作:
$self->{'_parser'} = XML::SAX::Expat::Incremental->new( Handler => MyHandler->new($self) );
其中,MyHandler
是一个简单的XML::SAX
样式的处理程序,现在可以访问表
调用Table::partial_emit
将执行以下操作:
my $buf;
my $bytes_to_read = 50; # small for testing
while (read($this->{'_fh'}, $buf, $bytes_to_read)) {
$this->{'_parser'}->parse_more($buf);
# MyHandler will increment this based on the number of rows (DETAIL nodes) encountered
if ($this->{'_rows_emitted'} >= $rows) {
$this->{'_finished'} = 0;
last;
}
}
上面的代码在边缘情况下可能有一些bug,但在我的测试中效果很好。稍后我需要对它进行适当的压力测试,看看它是否已准备好投入生产。该模块设计用于在这种情况下运行良好
它的副标题是“一个以树模式处理巨大XML文档的perl模块”。经过一番搜索,我发现了一个非常有用的老线程,它精确地描述了我需要的内容 我可以使用
XML::Parser::ExpatNB
实现所需的行为<如果有必要,code>XML::SAX::Expat::Incremental将把它封装到一个SAX接口中,但我想我不会费心了
下面是示例代码。它的性能足够好(比XML::Twig
快),所以我将使用它
use strict;
use warnings;
use XML::Parser::Expat;
my $parser = XML::Parser::ExpatNB->new();
$parser->setHandlers('Start' => \&start_element,
'End' => \&end_element,
'Char' => \&char_data);
my $read_size = 64 * 1024; # test to find optimal size
my $file_name = '../details.xml';
my $buf;
open(my $fh, '<', $file_name) or die $!;
binmode($fh);
my $bytes_read;
while ( $bytes_read = read($fh, $buf, $read_size) ) {
$parser->parse_more($buf);
}
$parser->parse_done();
die "Error: $!" unless defined($bytes_read);
close($fh);
很抱歉,我忘了提及-我喜欢Twig的风格,并在这个特定文档上尝试过它,但我在一个小时后放弃了等待,而SAX在一两分钟内完成了完整的解析(不构建表)。我明天会检查,以确保我们有最新的版本,看看哪里出了问题。如果可能的话,我更喜欢SAX,因为它看起来很轻。@NickP:您可能没有正确设置
XML::Twig
。您应该为要作为一个单元处理的XML元素设置回调(细枝处理程序),并确保刷新回调末尾的树,否则,整个树将被保存在内存中,XML::Twig
并不比XML::LibXML
或任何其他从XML数据构建整个树的模块好。除了Borodin所说的(我同意),XML::Twig还附带了一个名为XML\u split
的工具。它提供了几种将大文件拆分为几个小文件的方便方法(按深度、节点数或拆分文件的大小)。如果您选择这样做,这可能会对您有所帮助。我还应该提到,XML::LibXML::Reader可能也是一个不错的选择。@Borodin:XML::Twig现在可以工作了,因为我已经查看了我的脚本,但我遇到了相同的问题,即能够停止/恢复。请澄清“以后可以暂停并恢复吗?”XML::Twig
将等待您指定的回调返回。如果晚一点意味着几微秒,那么就没有问题,但如果可能是几年,那么你就有了一个不同的设计问题。这同样适用于您的BIG BROTHER主应用程序。你没有描述过任何让你不能等待十年或二十年才做出反应的事情。@Borodin pause是一个不正确的词,我不应该用它。屈服控制将更加准确。我们希望细枝在特定数量的节点后返回,恢复正常处理,然后再次调用拥有细枝的对象,它应该在停止读取的位置恢复读取。稍后我将尝试添加一个简化的示例。
use strict;
use warnings;
use XML::Parser::Expat;
my $parser = XML::Parser::ExpatNB->new();
$parser->setHandlers('Start' => \&start_element,
'End' => \&end_element,
'Char' => \&char_data);
my $read_size = 64 * 1024; # test to find optimal size
my $file_name = '../details.xml';
my $buf;
open(my $fh, '<', $file_name) or die $!;
binmode($fh);
my $bytes_read;
while ( $bytes_read = read($fh, $buf, $read_size) ) {
$parser->parse_more($buf);
}
$parser->parse_done();
die "Error: $!" unless defined($bytes_read);
close($fh);
my $reader = XML::LibXML::Reader->new(location => $file_name) or die $!;
while ($reader->read) {
processNode($reader);
}