2010-11-14 251 views
4

我有2個XML文件1 115MB大小,另一個爲34MB大小。解析大型XML文件?

Wiile讀取文件中的有1場稱爲遞減,與文件B的關係就在那裏我檢索文件B場id其中desc.file A是iqual到name.file B.

文件A已經太大,我不得不在文件B內搜索,並且需要很長時間才能完成。

我怎麼能加快這一proccess或什麼會是一個更好的計算策略做呢?

當前的代碼我使用:文件B.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> 

例如:文件A.XML的

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

例如

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

回答

4

這裏是例子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>"; 
+0

+1非常感謝信息學習的東西,真的很感謝你通過我的麻煩:) – Guapo 2010-11-15 21:37:35

0

我雖然不能幫助您瞭解Perl代碼的細節,處理大量XML數據時有一些通用的指導原則。一般來說,有2種XML API--基於DOM和基於Stream。之前,用戶級API變成「可用」基於DOM API的(如XML DOM)將在解析整個XML文檔到內存,而與基於流的API(如SAX)的實現並不需要解析整個XML文檔。基於Stream的解析器的一個好處是它們通常使用更少的內存,因爲它們不需要一次保存整個XML文檔 - 這在處理大型XML文檔時顯然是一件好事。看看這裏的XML :: Simple文檔,它似乎在那裏may be SAX support available - 你試過這個嗎?

+0

謝謝,我會看看它現在;) – Guapo 2010-11-14 16:24:35

+0

哪裏可以找到一些:: SAX ::過濾器?我已經安裝了XML :: SAX,並遵循該頁面教程,它使得Some :: SAX :: Filter的用法,但我無法得到它的保留。 – Guapo 2010-11-14 16:45:09

+0

如果您已經仔細閱讀了文檔,或者完全使用了該模塊,則會注意到XML :: Simple將整個文檔加載到內存中。你讀到的是它可以作爲一個SAX過濾器(從SAX流獲得它的輸入並輸出一個)。還有更多的XML解析DOM和流:第一個DOM只是其中一種樹處理類型,然後還有其他模式:XML :: LibXML具有拉模式,XML :: Twig在樹的部分。總的來說,這不是一個非常有用的答案。 – mirod 2010-11-20 11:38:16

0

我不是一個Perl的傢伙,所以藉此與一粒鹽,但我看到兩個問題:

  1. 您遍歷所有值的文件B,直到你的事實爲文件A中的每個元素找到正確的值是效率低下的。相反,你應該使用某種映射/字典來獲取文件B中的值。

  2. 它看起來像你在開始處理之前解析內存中的兩個文件。文件A最好作爲流處理,而不是將整個文檔加載到內存中。

+0

你可能會給我一個項目1的例子嗎?關於項目2我現在正在努力,謝謝。 – Guapo 2010-11-14 16:24:16

+0

@Guapo:看到這裏:http://www.cs.mcgill.ca/~abatko/computers/programming/perl/howto/hash/ – 2010-11-14 16:31:01

1
  1. 抓住所有的有趣的「遞減」從文件中的字段,把它們放在一個哈希值。您只需解析一次,但如果它仍然需要太長時間,請查看XML::Twig
  2. 解析文件B.一次,提取你需要的東西。使用散列。

看起來你只需要在XML文件的組成部分。 XML :: Twig只能解析您感興趣的元素,並使用「twig_roots」參數丟棄其餘元素。 XML ::簡單容易上手,但..

+0

但我需要更多來自fileA :)幾乎所有字段從fileA,和一個來自fileB的ID字段,通過desc名稱字段連接到文件A,文件A是115mb,而文件B是34mb,感謝答案。 – Guapo 2010-11-15 21:36:34