Разбор больших файлов XML? - PullRequest
4 голосов
/ 14 ноября 2010

У меня есть 2 xml-файла: 1 размером 115 МБ, а другой - размером 34 МБ.

При чтении файла A существует 1 поле с именем desc, которое связывает его с файлом B, где я получаю идентификатор поля из файла B, гдеdesc.file A не подходит для name.file B.

файл A уже слишком велик, поэтому мне нужно искать внутри файла B, и для его завершения требуется очень много времени.

Как могЯ ускоряю этот процесс или что может быть лучше для этого?

текущий код, который я использую:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Simple qw(:strict XMLin);

my $npcs = XMLin('Client/client_npcs.xml', KeyAttr => { }, ForceArray => [ 'npc_client' ]);
my $strings = XMLin('Client/client_strings.xml', KeyAttr => { }, ForceArray => [ 'string' ]);

my ($nameid,$rank);

open (my $fh, '>>', 'Output/npc_templates.xml');
print $fh "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<npc_templates xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"npcs.xsd\">\n";
foreach my $npc ( @{ $npcs->{npc_client} } ) {
        if (defined $npc->{desc}) {
                foreach my $string (@{$strings->{string}}) {
                        if (defined $string->{name} && $string->{name} =~ /$npc->{desc}/i) {
                                $nameid = $string->{id};
                                last;
                        }
                }
        } else {
                $nameid = "";
        }

        if (defined $npc->{hpgauge_level} && $npc->{hpgauge_level} > 25 && $npc->{hpgauge_level} < 28) {
            $rank = 'LEGENDARY';
        } elsif (defined $npc->{hpgauge_level} && $npc->{hpgauge_level} > 21 && $npc->{hpgauge_level} < 23) {
            $rank = 'HERO';
        } elsif (defined $npc->{hpgauge_level} && $npc->{hpgauge_level} > 10 && $npc->{hpgauge_level} < 15) {
            $rank = 'ELITE';
        } elsif (defined $npc->{hpgauge_level} && $npc->{hpgauge_level} > 0 && $npc->{hpgauge_level} < 11) {
            $rank = 'NORMAL';
        } else {
            $rank = $gauge;
        }

        print $fh qq|\t<npc_template npc_id="$npc->{id}" name="$npc->{name}" name_id="$nameid" height="$npc->{scale}" rank="$rank" tribe="$npc->{tribe}" race="$npc->{race_type}" hp_gauge="$npc->{hpgauge_level}"/>\n|;
}
print $fh "</<npc_templates>";
close($fh);

пример файла A.xml:

<?xml version="1.0" encoding="utf-16"?>
<npc_clients>
  <npc_client>
    <id>200000</id>
    <name>SkillZone</name>
    <desc>STR_NPC_NO_NAME</desc>
    <dir>Monster/Worm</dir>
    <mesh>Worm</mesh>
    <material>mat_mob_reptile</material>
    <show_dmg_decal>0</show_dmg_decal>
    <ui_type>general</ui_type>
    <cursor_type>none</cursor_type>
    <hide_path>0</hide_path>
    <erect>1</erect>
    <bound_radius>
      <front>1.200000</front>
      <side>3.456000</side>
      <upper>3.000000</upper>
    </bound_radius>
    <scale>10</scale>
    <weapon_scale>100</weapon_scale>
    <altitude>0.000000</altitude>
    <stare_angle>75.000000</stare_angle>
    <stare_distance>20.000000</stare_distance>
    <move_speed_normal_walk>0.000000</move_speed_normal_walk>
    <art_org_move_speed_normal_walk>0.000000</art_org_move_speed_normal_walk>
    <move_speed_normal_run>0.000000</move_speed_normal_run>
    <move_speed_combat_run>0.000000</move_speed_combat_run>
    <art_org_speed_combat_run>0.000000</art_org_speed_combat_run>
    <in_time>0.100000</in_time>
    <out_time>0.500000</out_time>
    <neck_angle>90.000000</neck_angle>
    <spine_angle>10.000000</spine_angle>
    <ammo_bone>Bip01 Head</ammo_bone>
    <ammo_fx>skill_stoneshard.stoneshard.ammo</ammo_fx>
    <ammo_speed>50</ammo_speed>
    <pushed_range>0.000000</pushed_range>
    <hpgauge_level>3</hpgauge_level>
    <magical_skill_boost>0</magical_skill_boost>
    <attack_delay>2000</attack_delay>
    <ai_name>SummonSkillArea</ai_name>
    <tribe>General</tribe>
    <pet_ai_name>Pet</pet_ai_name>
    <sensory_range>15.000000</sensory_range>
  </npc_client>
</npc_clients>

пример файла B.xml:

<?xml version="1.0" encoding="utf-16"?>
<strings>
  <string>
    <id>350000</id>
    <name>STR_NPC_NO_NAME</name>
    <body> </body>
  </string>
</strings>

Ответы [ 4 ]

4 голосов
/ 15 ноября 2010

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

use XML::Twig;

my %strings = ();
XML::Twig->new(
    twig_handlers => {
        'strings/string' => sub {
            $strings{ lc $_->first_child('name')->text }
                = $_->first_child('id')->text
        },
    }
)->parsefile('B.xml');

print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<npc_templates xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"npcs.xsd\">\n";
XML::Twig->new(
    twig_handlers => {
        'npc_client' => sub {
            my $nameid = eval { $strings{ lc $_->first_child('desc')->text } };

            # calculate rank as needed
            my $hpgauge_level = eval { $_->first_child('hpgauge_level')->text };
            $rank = $hpgauge_level >= 28 ? 'ERROR'
                  : $hpgauge_level  > 25 ? 'LEGENDARY'
                  : $hpgauge_level  > 21 ? 'HERO'
                  : $hpgauge_level  > 10 ? 'ELITE'
                  : $hpgauge_level  >  0 ? 'NORMAL'
                  :                        $hpgauge_level;

            my $npc_id    = eval { $_->first_child('id')->text };
            my $name      = eval { $_->first_child('name')->text };
            my $tribe     = eval { $_->first_child('tribe')->text };
            my $scale     = eval { $_->first_child('scale')->text };
            my $race_type = eval { $_->first_child('race_type')->text };
            print
                qq|\t<npc_template npc_id="$npc_id" name="$name" name_id="$nameid" height="$scale" rank="$rank" tribe="$tribe" race="$race_type" hp_gauge="$hpgauge_level"/>\n|;
            $_->purge;
        }
    }
)->parsefile('A.xml');
print "</<npc_templates>";
1 голос
/ 15 ноября 2010
  1. Соберите все интересные поля 'desc' из файла A и поместите их в хеш.Вам нужно только проанализировать его один раз, но если это все еще занимает слишком много времени, взгляните на XML :: Twig .
  2. .Используйте хеш.

Похоже, вам нужны только части XML-файлов.XML :: Twig может анализировать только те элементы, которые вам интересны, и отбрасывать остальные, используя параметр "twig_roots".XML :: Simple легче начать, хотя ..

0 голосов
/ 14 ноября 2010

Я не Perl парень, так что возьми это с крошкой соли, но я вижу 2 проблемы:

  1. Тот факт, что вы перебираете все значения в файле B, пока не найдете правильное значение для каждого элемента в файле A, неэффективен. Вместо этого вы должны использовать какую-то карту / словарь для значений в файле B.

  2. Похоже, вы анализируете оба файла в памяти еще до того, как приступили к обработке. Файл A лучше обрабатывать как поток, а не загружать весь документ в память.

0 голосов
/ 14 ноября 2010

Хотя я не могу помочь вам со спецификой вашего Perl-кода, есть некоторые общие рекомендации при работе с большими объемами XML-данных. Вообще говоря, существует 2 вида API-интерфейсов XML - на основе DOM и на основе Stream. API на основе Dom (например, XML DOM) будет анализировать весь XML-документ в памяти до того, как API уровня пользователя станет «доступным», тогда как при использовании API на основе потоков (например, SAX) реализации не нужно анализировать весь XML-документ , Одно из преимуществ синтаксических анализаторов на основе потоков состоит в том, что они обычно используют гораздо меньше памяти, поскольку им не нужно хранить весь XML-документ в памяти сразу - это, очевидно, хорошо при работе с большими XML-документами. Глядя на XML :: Simple документы здесь, кажется, там может быть доступна поддержка SAX - вы пробовали это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...