«Недостаточно памяти» при анализе большого (100 Мб) XML-файла с использованием perl - PullRequest
8 голосов
/ 03 сентября 2011

У меня ошибка " Недостаточно памяти " при анализе большого (100 Мб) XML-файла

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

Что я могу сделать, чтобы исправить эту ошибку?

Ответы [ 2 ]

18 голосов
/ 03 сентября 2011

Обработка больших файлов XML, которые не помещаются в памяти, - это то, что XML::Twig рекламирует :

Одной из сильных сторон XML::Twig является то, что он позволяет вам работать с файлами которые не помещаются в памяти (кстати, хранение XML-документа в памяти как дерево довольно дорогое, коэффициент расширения часто около 10).

Для этого вы можете определить обработчики, которые будут вызываться один раз конкретный элемент был полностью разобран. В этих обработчиках вы можете получить доступ к элементу и обработать его по своему усмотрению (...)


Код, размещенный в вопросе, вообще не использует силу XML::Twig (использование метода simplify не делает его намного лучше, чем XML::Simple).

Чего не хватает в коде, так это 'twig_handlers' или 'twig_roots', которые по сути заставляют синтаксический анализатор эффективно фокусироваться на соответствующих частях памяти XML-документа.

Сложно сказать, не видя XML, является ли обработка документа по частям или только выбранных частей подходящим вариантом, но любой из них должен решить эту проблему.

Таким образом, код должен выглядеть примерно так (демонстрация chunk-by-chunk):

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;
8 голосов
/ 03 сентября 2011

См. Обработка фрагмента XML-документа с помощью фрагмента в документации XML :: Twig , в нем конкретно обсуждается, как обрабатывать документ по частям, что позволяет обрабатывать большие файлы XML.

...