1

I have a bizarre XML document arranged in the following manner

<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
    ...
  <b>
    ....
  </b>
  <e/>
</a>

I want to extract the values of d2, d4, d5 for all the c nodes within all the b nodes.

I tried using XML::Simple and ran into a lot of difficulties with array referencing. I tried using XML::DOM, but considering my XML file is 500MB in size, it does not seem to be a good option. Please suggest a good approach as I'm new to Perl

4

3 に答える 3

2

あなたの質問は少し混乱しています。d要素ではなく、要素の属性が必要ですc。または、下の要素が何であるかに関係なく、属性の値が必要な場合がありますc

いずれにせよ、特にファイルが大きい場合、これは に適しているように見えますXML::Twig:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new( twig_handlers => { 'b/c/*' => \&get_atts })
         ->parse( \*DATA); # replace by parsefile( 'my.xml') 

sub get_atts
  { my( $t, $elt)= @_;
    foreach my $att ( qw( d2 d4 d5))
      { print "$att: ", $elt->att( $att), " "; }
    print "\n";
    $t->purge; # this frees the memory so you keep at most 1 d element 
  }

__DATA__
<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
  </b>
  <b>
  </b>
  <e/>
</a>

属性が常に要素内にある場合は、より効率的なにd置き換えます。'b/c/*''b/c/d'

于 2012-07-06T15:25:38.327 に答える
1

xsh の使用:

for a/b/c/d ls (@d2 | @d4 | @d5);

更新: (mirod の場合): Perl から XML::XSH2 を使用するのはエレガントではありませんが、それでも動作します -

#!/usr/bin/perl
use strict;
use warnings;

use XML::XSH2;

xsh q{
    open 1.xml ;
    for /a/b/c/d {
        for my $attr in (@d2 | @d4 | @d5) {
            perl { push @ar, $attr }
        }
    }
};

printf "%s:%s\n", $_->name, $_->value for @XML::XSH2::Map::ar;

または、Perl に xsh コードを書いてもらいます。

#!/usr/bin/perl
use warnings;
use strict;

use XML::XSH2;

xsh 'open 1.xml';
xsh '$attributes = (' . join('|', map 'a/b/c/@d' . $_, 1, 2, 4) . ')';
for (@$XML::XSH2::Map::attributes) {
    print $_->name, '=', $_->value, "\n";
}
于 2012-07-06T15:42:20.767 に答える
1

CPAN にはこれを支援する XML モジュールが多数ありますが、この場合、XML::XPathXML から抽出したいデータを簡潔に記述することができるので、私のお金は有効です。

このプログラムは、サンプル データを使用し、必要と思われる出力を提供します (厳密にはどのノードd="xx"にも属性はありません)。<c>

use strict;
use warnings;

use feature 'say';

use XML::XPath;

my $xml = XML::XPath->new(ioref => \*DATA);

for my $cnode ($xml->find('//b/c/d')->get_nodelist) {
  for ($cnode->find('@d2|@d4|@d5')->get_nodelist) {
    print $_->getData, "\n";
  }
}

__DATA__
<a>
   <b>
     <c c1="blah" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
     <c c1="blahc" c2="blah">
        <d d1="blah0" d2="blah1" d3="blah2" d4="blah3" d5="blah4" />
        <d d1="blah5" d2="blah6" d3="blah7" d4="blah8" d5="blah9" />
        <d d1="blah10" d2="blah11" d3="blah12" d4="blah13" d5="blah14" />
     </c>
    ...
  </b>
  <e/>
</a>

出力

blah1
blah3
blah4
blah6
blah8
blah9
blah11
blah13
blah14
blah1
blah3
blah4
blah6
blah8
blah9
blah11
blah13
blah14
于 2012-07-06T17:02:57.930 に答える