8

大きな (100 Mb) 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;
}

このエラーの問題を解決するにはどうすればよいですか?

4

2 に答える 2

18

メモリに収まらない大きな XML ファイルを処理することは、アドバタイズするものです。XML::Twig

の長所の 1 つは、XML::Twigメモリに収まらないファイルを操作できることです (ところで、XML ドキュメントをツリーとしてメモリに格納すると、メモリを大量に消費します。拡張係数は多くの場合 10 前後です)。

これを行うには、特定の要素が完全に解析されると呼び出されるハンドラを定義できます。これらのハンドラーでは、要素にアクセスして、必要に応じて処理できます (...)


質問に投稿されたコードは、 の強度をXML::Twigまったく利用してsimplifyいません (メソッドを使用しても、 よりもはるかに優れているわけではありませんXML::Simple)。

コードに欠けているのは ' twig_handlers' または ' twig_roots' です。これにより、パーサーは XML ドキュメントの関連部分にメモリ効率よく集中できます。

ドキュメントをチャンクごとに処理するか、選択した部分のみを処理するかを XML を見ずに判断するのは困難ですが、どちらでもこの問題は解決するはずです。

したがって、コードは次のようになります (チャンクごとのデモ)。

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;
于 2011-09-03T19:06:06.183 に答える
8

XML::Twigドキュメントの「 Processing an XML document chunk by chunk」セクションを参照してください。具体的には、ドキュメントをパーツごとに処理して、大きな XML ファイルの処理を可能にする方法について説明しています。

于 2011-09-03T18:54:02.783 に答える