我有一个奇怪的XML文档,按以下方式排列
<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>
我想为所有d2
个节点中的所有d4
个节点提取d5
,c
,b
的值。
我尝试使用XML::Simple
并在阵列引用方面遇到了很多困难。
我尝试使用XML::DOM
,但考虑到我的XML文件大小为500MB,它似乎不是一个好选择。我建议一个好方法,因为我是Perl的新手
答案 0 :(得分: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'
,这样会更有效。
答案 1 :(得分: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";
}
答案 2 :(得分:1)
CPAN中有许多XML模块可以帮助您解决这个问题,但在这种情况下我的资金是XML::XPath
,它允许您简洁地描述您想要从XML中提取的数据。
此程序使用您的示例数据并提供我认为您想要的输出(尽管严格来说,任何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