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);
}