2011-09-03 61 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;