2010-11-14 11 views
4

J'ai 2 fichiers xml 1 avec une taille de 115mb et un autre avec une taille de 34mb.Analyse de fichiers XML volumineux?

Wiile fichier de lecture A il y a 1 champ appelé desc que les relations avec elle le fichier B où je récupérer l'ID du champ de fichier B où desc.file A est IQUAL à name.file B.

fichier A est déjà trop grand alors je dois chercher dans le fichier B et cela prend beaucoup de temps.

Comment pourrais-je accélérer ce processus ou quelle serait une meilleure approche pour le faire?

code actuel J'utilise:

#!/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); 

exemple de fichier 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> 

exemple de fichier B.xml:

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

Répondre

4

Voici un exemple d'utilisation XML::Twig. Le principal avantage est qu'il ne contient pas de fichier entier en mémoire, donc le traitement est beaucoup plus rapide. Le code ci-dessous essaye d'émuler le fonctionnement du script de la question.

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>"; 
+0

+1 Merci beaucoup de choses d'apprentissage très instructif vraiment apprécier pour vous passer mes ennuis :) – Guapo

0

Bien que je ne peux pas vous aider avec les spécificités de votre code Perl, il y a quelques directives générales lorsqu'il s'agit de gros volumes de données XML. Il y a, en gros, 2 sortes d'API XML - DOM et Stream. Les API basées sur Dom (comme XML DOM) vont analyser un document XML entier en mémoire avant que l'API au niveau utilisateur ne devienne disponible, alors qu'avec une API basée sur un flux (comme SAX) l'implémentation n'a pas besoin d'analyser tout le document XML . L'un des avantages des analyseurs basés sur les flux est qu'ils utilisent généralement moins de mémoire, car ils n'ont pas besoin de conserver simultanément tout le document XML en mémoire. C'est évidemment une bonne chose pour les documents XML volumineux. En regardant les documents XML :: Simple ici, il semble y may be SAX support available - avez-vous essayé cela?

+0

Merci je vais jeter un coup d'oeil à ce moment;) – Guapo

+0

où puis-je trouver Some :: SAX :: Filter? J'ai installé XML :: SAX et suivant ce tutoriel de page il fait l'utilisation de Some :: SAX :: Filter mais je ne peux pas l'obtenir. – Guapo

+0

Si vous aviez lu les docs un peu plus attentivement, ou utilisé le module, vous auriez remarqué que XML :: Simple charge le document entier en mémoire. Ce que vous lisez est qu'il peut agir comme un filtre SAX (obtenir sa entrée d'un flux SAX et sortie un). Il y a aussi plus d'XML à analyser DOM et stream: le premier DOM est juste un type d'arbre, il y en a d'autres, puis il y a encore d'autres modes: XML :: LibXML a un mode pull, et XML :: Twig parties de l'arbre. Donc, dans l'ensemble, ce n'était pas une réponse très utile. – mirod

0

Je ne suis pas un gars perl, donc prendre avec un grain de sel, mais je vois 2 problèmes:

  1. Le fait que vous itérez toutes les valeurs dans le fichier B jusqu'à ce que vous trouver la valeur correcte pour chaque élément dans le fichier A est inefficace. Au lieu de cela, vous devriez utiliser une sorte de carte/dictionnaire pour les valeurs dans le fichier B.

  2. Il semble que vous analysez les deux fichiers en mémoire avant même de commencer le traitement. Le fichier A serait mieux traité en tant que flux au lieu de charger le document entier en mémoire.

+0

pourriez-vous peut-être me donner un exemple de l'article 1? à propos du point 2 Je travaille dessus maintenant merci. – Guapo

+0

@Guapo: voir ici: http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/hash/ –

1
  1. saisir tous les champs intéressants 'desc' à partir du fichier A et les mettre dans une table de hachage. Il suffit de l'analyser une fois, mais si cela prend trop de temps, jetez un œil à XML::Twig.
  2. Parse fichier B. une fois et extraire les choses dont vous avez besoin. Utilisez le hachage.

On dirait que vous n'avez besoin que de parties des fichiers xml. XML :: Twig ne peut analyser que les éléments qui vous intéressent et rejeter le reste en utilisant le paramètre "twig_roots". XML :: Simple est plus facile à démarrer avec si ..

+0

mais j'en ai besoin de plus de fileA :) presque tous les champs de fileA, et un Le champ ID du fichier B qui est connecté au fichier A par le champ de nom de desc et le fichier A est de 115 Mo alors que le fichier B est d'environ 34 Mo, merci pour la réponse. – Guapo