使用PerlXML::LibXML处理XML的速度太慢

使用PerlXML::LibXML处理XML的速度太慢,xml,perl,Xml,Perl,XML文件如下所示: <?xml version="1.0" encoding="UTF-8"?> <resource-data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="resource-data.xsd"> <class name="AP"> <attributes> <resourceI

XML文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<resource-data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="resource-data.xsd">
  <class name="AP">
    <attributes>
      <resourceId>00 11 B5 1B 6D 20</resourceId>
      <lastModifyTime>20130107091545</lastModifyTime>
      <dcTime>20130107093019</dcTime>
      <attribute name="NMS_ID" value="DNMS" />
      <attribute name="IP_ADDR" value="10.11.141.111" />
      <attribute name="LABEL_DEV" value="00 11 B5 1B 6D 20" />
    </attributes>
        <attributes>
      <resourceId>00 11 B5 1B 6D 21</resourceId>
      <lastModifyTime>20130107091546</lastModifyTime>
      <dcTime>20130107093019</dcTime>
      <attribute name="NMS_ID" value="DNMS" />
      <attribute name="IP_ADDR" value="10.11.141.112" />
      <attribute name="LABEL_DEV" value="00 11 B5 1B 6D 21" />
    </attributes>
  </class>
</resource-data>

我的问题是,如果XML文件只有80MB,那么程序会非常快,但当XML文件大得多时,程序会非常慢。有人能帮我加快速度吗?

如果你有这么大的XML文件-80MB+,你无法将整个文件解析到内存中-首先,它非常慢,其次,它最终会耗尽内存,你的程序会崩溃

我建议您使用和使用回调重写代码。

使用将允许您在解析过程中处理每个
元素,然后丢弃不再需要的XML数据

这个程序似乎能满足你的需要

use strict;
use warnings;

use XML::Twig;
use Encode;

use constant XML_FILE => 'S:/AP_201301073100_1.xml';
use constant OUT_FILE => 'D:/ap.txt';

open my $outfh, '>:encoding(gbk)', OUT_FILE or die $!;

my $twig = XML::Twig->new(twig_handlers => {attributes => \&attributes});
$twig->parsefile('myxml.xml');

sub attributes {
  my ($twig, $atts) = @_;
  my @values = map $_->att('value'), $atts->children('attribute');
  print $outfh join("\t", @values), "\n";
  $twig->purge;
}
输出

DNMS  10.11.141.111 00 11 B5 1B 6D 20
DNMS  10.11.141.112 00 11 B5 1B 6D 21

对于大型XML文件,必须使用基于流的解析器,如,因为DOM解析器在内存中构建整个XML结构。

另一种可能性是使用。它的工作原理与SAX类似,但使用与XML::libxml相同的
libxml
库:

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML::Reader;

my $reader = XML::LibXML::Reader->new(location => '1.xml');

open my $OUT, '>:encoding(gbk)', '1.out';

while ($reader->read) {
    attr($reader) if 'attributes' eq $reader->name
                     and XML_READER_TYPE_ELEMENT == $reader->nodeType;
}

sub attr {
    my $reader = shift;
    my @kids;
  ATTRIBUTE:
    while ($reader->read) {
        my $name = $reader->name;
        last ATTRIBUTE if 'attributes' eq $name;
        next ATTRIBUTE if XML_READER_TYPE_END_ELEMENT == $reader->nodeType;
        push @kids, $reader->getAttribute('value')
            if 'attribute' eq $name;
    }
    print {$OUT} join("\t", @kids), "\n";
}
另一种方式是:


我认为大型xml文件建议使用基于语法的解析器。我只需要知道如何修改我的程序,你能帮我吗。顺便说一句,使用
openin,“>:encoding(gbk)”,$file\u data
而不是到处编码。@John很抱歉,我在XML解析方面经验有限,在SAX解析器方面一点经验都没有,也许你可以在谷歌上搜索教程。是一个开始。您必须始终在程序的开头使用strict和warnings,并在第一个使用点使用my声明所有变量。对于输出流来说,中的
似乎是个奇怪的名字!谢谢,这种方式非常快,只需大约一分钟就可以将xml文件转换为txt。谢谢,我已经测试过了,它可以运行,但比choroba的答案需要更多的时间。谢谢,你们两个都很出色
#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML::Reader;

my $reader = XML::LibXML::Reader->new(location => '1.xml');

open my $OUT, '>:encoding(gbk)', '1.out';

while ($reader->read) {
    attr($reader) if 'attributes' eq $reader->name
                     and XML_READER_TYPE_ELEMENT == $reader->nodeType;
}

sub attr {
    my $reader = shift;
    my @kids;
  ATTRIBUTE:
    while ($reader->read) {
        my $name = $reader->name;
        last ATTRIBUTE if 'attributes' eq $name;
        next ATTRIBUTE if XML_READER_TYPE_END_ELEMENT == $reader->nodeType;
        push @kids, $reader->getAttribute('value')
            if 'attribute' eq $name;
    }
    print {$OUT} join("\t", @kids), "\n";
}
use strict;
use warnings;

use XML::Rules;
use Data::Dumper;

my @rules = (
  attribute => [ attributes => sub { print "$_[1]{value}\n"; return } ],
  _default => undef,
);

my $xr = XML::Rules->new( rules => \@rules );
my $data = $xr->parse($xml);