2011-09-03 60 views
8

我有一个错误“出的内存”在分析大解析大(100 MB)的XML文件(100 MB)的XML文件“内存不足”,而用perl

use strict; 
use warnings; 
use XML::Twig; 

my $twig=XML::Twig->new(); 
my $data = XML::Twig->new 
      ->parsefile("divisionhouserooms-v3.xml") 
       ->simplify(keyattr => []); 

my @good_division_numbers = qw(30 31 32 35 38); 

foreach my $property (@{ $data->{DivisionHouseRoom}}) { 

    my $house_code = $property->{HouseCode}; 
    print $house_code, "\n"; 

    my $amount_of_bedrooms = 0; 

    foreach my $division (@{ $property->{Divisions}->{Division} }) { 

     next unless grep { $_ eq $division->{DivisionNumber} } @good_division_numbers; 
     $amount_of_bedrooms += $division->{DivisionQuantity}; 
    } 

    open my $fh, ">>", "Result.csv" or die $!; 
    print $fh join("\t", $house_code, $amount_of_bedrooms), "\n"; 
    close $fh; 
} 

我能做些什么解决这个错误问题?

+5

对于大的XML文件,你应该依靠面向事件的解析器,像SAX。我不知道perl,但是你知道是否有类似的东西吗? –

+2

我不知道这个模块,但在[CPAN](http://search.cpan.org/perldoc?XML::Twig)上有提及如何处理小文件和大文件,你在这里是版本为“小”。所以也许你可能会将代码调整为“巨大”的实现。 – TLP

+1

@Rubens - 请看下面的优秀答案,但简短的版本是“无可挑剔的,Perl有SAX解析器”。 – DVK

回答

18

处理,不适合在大内存的XML文件是什么,XML::Twigadvertises

一个XML::Twig的优势在于,它让你的文件 不适合在内存中工作(BTW将XML文档作为 树存储在内存中的内存相当昂贵,扩展因子通常是 约10)。

要做到这一点,您可以定义处理程序,一旦 特定元素已被完全解析,将被调用。在这些处理器可以 访问的元素和处理它,你看到所有的拟合(...)


张贴在问题的代码而不利用XML::Twig强度(用simplify方法并不比XML::Simple好)。

代码中缺少的是'twig_handlers'或'twig_roots',这本质上会导致解析器高效地关注XML文档的相关部分。

很难说没有看到XML是否processing the document chunk-by-chunkjust selected parts是要走的路,但任一个都应该解决这个问题。

因此,代码应该看起来像以下(块逐块演示):

use strict; 
use warnings; 
use XML::Twig; 
use List::Util 'sum'; # To make life easier 
use Data::Dump 'dump'; # To see what's going on 

my %bedrooms;   # Data structure to store the wanted info 

my $xml = XML::Twig->new (
          twig_roots => { 
              DivisionHouseRoom => \&count_bedrooms, 
             } 
         ); 

$xml->parsefile('divisionhouserooms-v3.xml'); 

sub count_bedrooms { 

    my ($twig, $element) = @_; 

    my @divParents = $element->children('Divisions'); 
    my $id = $element->first_child_text('HouseCode'); 

    for my $divParent (@divParents) { 
     my @divisions = $divParent->children('Division'); 
     my $total = sum map { $_->text } @divisions; 
     $bedrooms{$id} = $total; 
    } 

    $element->purge; # Free up memory 
} 

dump \%bedrooms;